| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 | 
							- #!/bin/bash
 
- #
 
- # Bash completion generated for '{{name}}' at {{date}}.
 
- #
 
- # The original template lives here:
 
- # https://github.com/trentm/node-dashdash/blob/master/etc/dashdash.bash_completion.in
 
- #
 
- #
 
- # Copyright 2016 Trent Mick
 
- # Copyright 2016 Joyent, Inc.
 
- #
 
- #
 
- # A generic Bash completion driver script.
 
- #
 
- # This is meant to provide a re-usable chunk of Bash to use for
 
- # "etc/bash_completion.d/" files for individual tools. Only the "Configuration"
 
- # section with tool-specific info need differ. Features:
 
- #
 
- # - support for short and long opts
 
- # - support for knowing which options take arguments
 
- # - support for subcommands (e.g. 'git log <TAB>' to show just options for the
 
- #   log subcommand)
 
- # - does the right thing with "--" to stop options
 
- # - custom optarg and arg types for custom completions
 
- # - (TODO) support for shells other than Bash (tcsh, zsh, fish?, etc.)
 
- #
 
- #
 
- # Examples/design:
 
- #
 
- # 1. Bash "default" completion. By default Bash's 'complete -o default' is
 
- #    enabled. That means when there are no completions (e.g. if no opts match
 
- #    the current word), then you'll get Bash's default completion. Most notably
 
- #    that means you get filename completion. E.g.:
 
- #       $ tool ./<TAB>
 
- #       $ tool READ<TAB>
 
- #
 
- # 2. all opts and subcmds:
 
- #       $ tool <TAB>
 
- #       $ tool -v <TAB>     # assuming '-v' doesn't take an arg
 
- #       $ tool -<TAB>       # matching opts
 
- #       $ git lo<TAB>       # matching subcmds
 
- #
 
- #    Long opt completions are given *without* the '=', i.e. we prefer space
 
- #    separated because that's easier for good completions.
 
- #
 
- # 3. long opt arg with '='
 
- #       $ tool --file=<TAB>
 
- #       $ tool --file=./d<TAB>
 
- #    We maintain the "--file=" prefix. Limitation: With the attached prefix
 
- #    the 'complete -o filenames' doesn't know to do dirname '/' suffixing. Meh.
 
- #
 
- # 4. envvars:
 
- #       $ tool $<TAB>
 
- #       $ tool $P<TAB>
 
- #    Limitation: Currently only getting exported vars, so we miss "PS1" and
 
- #    others.
 
- #
 
- # 5. Defer to other completion in a subshell:
 
- #       $ tool --file $(cat ./<TAB>
 
- #    We get this from 'complete -o default ...'.
 
- #
 
- # 6. Custom completion types from a provided bash function.
 
- #       $ tool --profile <TAB>        # complete available "profiles"
 
- #
 
- #
 
- # Dev Notes:
 
- # - compgen notes, from http://unix.stackexchange.com/questions/151118/understand-compgen-builtin-command
 
- # - https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html
 
- #
 
- # Debugging this completion:
 
- #   1. Uncomment the "_{{name}}_log_file=..." line.
 
- #   2. 'tail -f /var/tmp/dashdash-completion.log' in one terminal.
 
- #   3. Re-source this bash completion file.
 
- #_{{name}}_log=/var/tmp/dashdash-completion.log
 
- function _{{name}}_completer {
 
-     # ---- cmd definition
 
-     {{spec}}
 
-     # ---- locals
 
-     declare -a argv
 
-     # ---- support functions
 
-     function trace {
 
-         [[ -n "$_{{name}}_log" ]] && echo "$*" >&2
 
-     }
 
-     function _dashdash_complete {
 
-         local idx context
 
-         idx=$1
 
-         context=$2
 
-         local shortopts longopts optargs subcmds allsubcmds argtypes
 
-         shortopts="$(eval "echo \${cmd${context}_shortopts}")"
 
-         longopts="$(eval "echo \${cmd${context}_longopts}")"
 
-         optargs="$(eval "echo \${cmd${context}_optargs}")"
 
-         subcmds="$(eval "echo \${cmd${context}_subcmds}")"
 
-         allsubcmds="$(eval "echo \${cmd${context}_allsubcmds}")"
 
-         IFS=', ' read -r -a argtypes <<< "$(eval "echo \${cmd${context}_argtypes}")"
 
-         trace ""
 
-         trace "_dashdash_complete(idx=$idx, context=$context)"
 
-         trace "  shortopts: $shortopts"
 
-         trace "  longopts: $longopts"
 
-         trace "  optargs: $optargs"
 
-         trace "  subcmds: $subcmds"
 
-         trace "  allsubcmds: $allsubcmds"
 
-         # Get 'state' of option parsing at this COMP_POINT.
 
-         # Copying "dashdash.js#parse()" behaviour here.
 
-         local state=
 
-         local nargs=0
 
-         local i=$idx
 
-         local argtype
 
-         local optname
 
-         local prefix
 
-         local word
 
-         local dashdashseen=
 
-         while [[ $i -lt $len && $i -le $COMP_CWORD ]]; do
 
-             argtype=
 
-             optname=
 
-             prefix=
 
-             word=
 
-             arg=${argv[$i]}
 
-             trace "  consider argv[$i]: '$arg'"
 
-             if [[ "$arg" == "--" && $i -lt $COMP_CWORD ]]; then
 
-                 trace "    dashdash seen"
 
-                 dashdashseen=yes
 
-                 state=arg
 
-                 word=$arg
 
-             elif [[ -z "$dashdashseen" && "${arg:0:2}" == "--" ]]; then
 
-                 arg=${arg:2}
 
-                 if [[ "$arg" == *"="* ]]; then
 
-                     optname=${arg%%=*}
 
-                     val=${arg##*=}
 
-                     trace "    long opt: optname='$optname' val='$val'"
 
-                     state=arg
 
-                     argtype=$(echo "$optargs" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
 
-                     word=$val
 
-                     prefix="--$optname="
 
-                 else
 
-                     optname=$arg
 
-                     val=
 
-                     trace "    long opt: optname='$optname'"
 
-                     state=longopt
 
-                     word=--$optname
 
-                     if [[ "$optargs" == *"-$optname="* && $i -lt $COMP_CWORD ]]; then
 
-                         i=$(( $i + 1 ))
 
-                         state=arg
 
-                         argtype=$(echo "$optargs" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
 
-                         word=${argv[$i]}
 
-                         trace "    takes arg (consume argv[$i], word='$word')"
 
-                     fi
 
-                 fi
 
-             elif [[ -z "$dashdashseen" && "${arg:0:1}" == "-" ]]; then
 
-                 trace "    short opt group"
 
-                 state=shortopt
 
-                 word=$arg
 
-                 local j=1
 
-                 while [[ $j -lt ${#arg} ]]; do
 
-                     optname=${arg:$j:1}
 
-                     trace "    consider index $j: optname '$optname'"
 
-                     if [[ "$optargs" == *"-$optname="* ]]; then
 
-                         argtype=$(echo "$optargs" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
 
-                         if [[ $(( $j + 1 )) -lt ${#arg} ]]; then
 
-                             state=arg
 
-                             word=${arg:$(( $j + 1 ))}
 
-                             trace "      takes arg (rest of this arg, word='$word', argtype='$argtype')"
 
-                         elif [[ $i -lt $COMP_CWORD ]]; then
 
-                             state=arg
 
-                             i=$(( $i + 1 ))
 
-                             word=${argv[$i]}
 
-                             trace "    takes arg (word='$word', argtype='$argtype')"
 
-                         fi
 
-                         break
 
-                     fi
 
-                     j=$(( $j + 1 ))
 
-                 done
 
-             elif [[ $i -lt $COMP_CWORD && -n "$arg" ]] && $(echo "$allsubcmds" | grep -w "$arg" >/dev/null); then
 
-                 trace "    complete subcmd: recurse _dashdash_complete"
 
-                 _dashdash_complete $(( $i + 1 )) "${context}__${arg/-/_}"
 
-                 return
 
-             else
 
-                 trace "    not an opt or a complete subcmd"
 
-                 state=arg
 
-                 word=$arg
 
-                 nargs=$(( $nargs + 1 ))
 
-                 if [[ ${#argtypes[@]} -gt 0 ]]; then
 
-                     argtype="${argtypes[$(( $nargs - 1 ))]}"
 
-                     if [[ -z "$argtype" ]]; then
 
-                         # If we have more args than argtypes, we use the
 
-                         # last type.
 
-                         argtype="${argtypes[@]: -1:1}"
 
-                     fi
 
-                 fi
 
-             fi
 
-             trace "    state=$state prefix='$prefix' word='$word'"
 
-             i=$(( $i + 1 ))
 
-         done
 
-         trace "  parsed: state=$state optname='$optname' argtype='$argtype' prefix='$prefix' word='$word' dashdashseen=$dashdashseen"
 
-         local compgen_opts=
 
-         if [[ -n "$prefix" ]]; then
 
-             compgen_opts="$compgen_opts -P $prefix"
 
-         fi
 
-         case $state in
 
-         shortopt)
 
-             compgen $compgen_opts -W "$shortopts $longopts" -- "$word"
 
-             ;;
 
-         longopt)
 
-             compgen $compgen_opts -W "$longopts" -- "$word"
 
-             ;;
 
-         arg)
 
-             # If we don't know what completion to do, then emit nothing. We
 
-             # expect that we are running with:
 
-             #       complete -o default ...
 
-             # where "default" means: "Use Readline's default completion if
 
-             # the compspec generates no matches." This gives us the good filename
 
-             # completion, completion in subshells/backticks.
 
-             #
 
-             # We cannot support an argtype="directory" because
 
-             #       compgen -S '/' -A directory -- "$word"
 
-             # doesn't give a satisfying result. It doesn't stop at the trailing '/'
 
-             # so you cannot descend into dirs.
 
-             if [[ "${word:0:1}" == '$' ]]; then
 
-                 # By default, Bash will complete '$<TAB>' to all envvars. Apparently
 
-                 # 'complete -o default' does *not* give us that. The following
 
-                 # gets *close* to the same completions: '-A export' misses envvars
 
-                 # like "PS1".
 
-                 trace "  completing envvars"
 
-                 compgen $compgen_opts -P '$' -A export -- "${word:1}"
 
-             elif [[ -z "$argtype" ]]; then
 
-                 # Only include opts in completions if $word is not empty.
 
-                 # This is to avoid completing the leading '-', which foils
 
-                 # using 'default' completion.
 
-                 if [[ -n "$dashdashseen" ]]; then
 
-                     trace "  completing subcmds, if any (no argtype, dashdash seen)"
 
-                     compgen $compgen_opts -W "$subcmds" -- "$word"
 
-                 elif [[ -z "$word" ]]; then
 
-                     trace "  completing subcmds, if any (no argtype, empty word)"
 
-                     compgen $compgen_opts -W "$subcmds" -- "$word"
 
-                 else
 
-                     trace "  completing opts & subcmds (no argtype)"
 
-                     compgen $compgen_opts -W "$shortopts $longopts $subcmds" -- "$word"
 
-                 fi
 
-             elif [[ $argtype == "none" ]]; then
 
-                 # We want *no* completions, i.e. some way to get the active
 
-                 # 'complete -o default' to not do filename completion.
 
-                 trace "  completing 'none' (hack to imply no completions)"
 
-                 echo "##-no-completion- -results-##"
 
-             elif [[ $argtype == "file" ]]; then
 
-                 # 'complete -o default' gives the best filename completion, at least
 
-                 # on Mac.
 
-                 trace "  completing 'file' (let 'complete -o default' handle it)"
 
-                 echo ""
 
-             elif ! type complete_$argtype 2>/dev/null >/dev/null; then
 
-                 trace "  completing '$argtype' (fallback to default b/c complete_$argtype is unknown)"
 
-                 echo ""
 
-             else
 
-                 trace "  completing custom '$argtype'"
 
-                 completions=$(complete_$argtype "$word")
 
-                 if [[ -z "$completions" ]]; then
 
-                     trace "  no custom '$argtype' completions"
 
-                     # These are in ascii and "dictionary" order so they sort
 
-                     # correctly.
 
-                     echo "##-no-completion- -results-##"
 
-                 else
 
-                     echo $completions
 
-                 fi
 
-             fi
 
-             ;;
 
-         *)
 
-             trace "  unknown state: $state"
 
-             ;;
 
-         esac
 
-     }
 
-     trace ""
 
-     trace "-- $(date)"
 
-     #trace "\$IFS: '$IFS'"
 
-     #trace "\$@: '$@'"
 
-     #trace "COMP_WORDBREAKS: '$COMP_WORDBREAKS'"
 
-     trace "COMP_CWORD: '$COMP_CWORD'"
 
-     trace "COMP_LINE: '$COMP_LINE'"
 
-     trace "COMP_POINT: $COMP_POINT"
 
-     # Guard against negative COMP_CWORD. This is a Bash bug at least on
 
-     # Mac 10.10.4's bash. See
 
-     # <https://lists.gnu.org/archive/html/bug-bash/2009-07/msg00125.html>.
 
-     if [[ $COMP_CWORD -lt 0 ]]; then
 
-         trace "abort on negative COMP_CWORD"
 
-         exit 1;
 
-     fi
 
-     # I don't know how to do array manip on argv vars,
 
-     # so copy over to argv array to work on them.
 
-     shift   # the leading '--'
 
-     i=0
 
-     len=$#
 
-     while [[ $# -gt 0 ]]; do
 
-         argv[$i]=$1
 
-         shift;
 
-         i=$(( $i + 1 ))
 
-     done
 
-     trace "argv: '${argv[@]}'"
 
-     trace "argv[COMP_CWORD-1]: '${argv[$(( $COMP_CWORD - 1 ))]}'"
 
-     trace "argv[COMP_CWORD]: '${argv[$COMP_CWORD]}'"
 
-     trace "argv len: '$len'"
 
-     _dashdash_complete 1 ""
 
- }
 
- # ---- mainline
 
- # Note: This if-block to help work with 'compdef' and 'compctl' is
 
- # adapted from 'npm completion'.
 
- if type complete &>/dev/null; then
 
-     function _{{name}}_completion {
 
-         local _log_file=/dev/null
 
-         [[ -z "$_{{name}}_log" ]] || _log_file="$_{{name}}_log"
 
-         COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
 
-             COMP_LINE="$COMP_LINE" \
 
-             COMP_POINT="$COMP_POINT" \
 
-             _{{name}}_completer -- "${COMP_WORDS[@]}" \
 
-             2>$_log_file)) || return $?
 
-     }
 
-     complete -o default -F _{{name}}_completion {{name}}
 
- elif type compdef &>/dev/null; then
 
-     function _{{name}}_completion {
 
-         local _log_file=/dev/null
 
-         [[ -z "$_{{name}}_log" ]] || _log_file="$_{{name}}_log"
 
-         compadd -- $(COMP_CWORD=$((CURRENT-1)) \
 
-             COMP_LINE=$BUFFER \
 
-             COMP_POINT=0 \
 
-             _{{name}}_completer -- "${words[@]}" \
 
-             2>$_log_file)
 
-     }
 
-     compdef _{{name}}_completion {{name}}
 
- elif type compctl &>/dev/null; then
 
-     function _{{name}}_completion {
 
-         local cword line point words si
 
-         read -Ac words
 
-         read -cn cword
 
-         let cword-=1
 
-         read -l line
 
-         read -ln point
 
-         local _log_file=/dev/null
 
-         [[ -z "$_{{name}}_log" ]] || _log_file="$_{{name}}_log"
 
-         reply=($(COMP_CWORD="$cword" \
 
-             COMP_LINE="$line" \
 
-             COMP_POINT="$point" \
 
-             _{{name}}_completer -- "${words[@]}" \
 
-             2>$_log_file)) || return $?
 
-     }
 
-     compctl -K _{{name}}_completion {{name}}
 
- fi
 
- ##
 
- ## This is a Bash completion file for the '{{name}}' command. You can install
 
- ## with either:
 
- ##
 
- ##     cp FILE /usr/local/etc/bash_completion.d/{{name}}   # Mac
 
- ##     cp FILE /etc/bash_completion.d/{{name}}             # Linux
 
- ##
 
- ## or:
 
- ##
 
- ##     cp FILE > ~/.{{name}}.completion
 
- ##     echo "source ~/.{{name}}.completion" >> ~/.bashrc
 
- ##
 
 
  |