diff options
author | Freddy Vulto <fvulto@gmail.com> | 2010-06-09 22:37:02 +0200 |
---|---|---|
committer | Freddy Vulto <fvulto@gmail.com> | 2010-06-09 22:37:02 +0200 |
commit | a8dd58cfa9a9e409053628c62f36880928b3e1c6 (patch) | |
tree | 4567496c8bdd7866d378597af6a2e048648842e6 | |
parent | f894f07500af8734a3c855f381c8382751463a45 (diff) | |
download | bash-completion-a8dd58cfa9a9e409053628c62f36880928b3e1c6.tar.gz |
Added `_upvars' and `_upvar'.
These helper methods aid in passing variables by reference.
-rw-r--r-- | bash_completion | 208 | ||||
-rw-r--r-- | contrib/screen | 6 | ||||
-rw-r--r-- | test/unit/_get_comp_words_by_ref.exp | 2 |
3 files changed, 123 insertions, 93 deletions
diff --git a/bash_completion b/bash_completion index 0d329f8a..e427bd2f 100644 --- a/bash_completion +++ b/bash_completion @@ -200,6 +200,83 @@ dequote() } +# Assign variable one scope above the caller +# Usage: local "$1" && _upvar $1 "value(s)" +# Param: $1 Variable name to assign value to +# Param: $* Value(s) to assign. If multiple values, an array is +# assigned, otherwise a single value is assigned. +# NOTE: For assigning multiple variables, use '_upvars'. Do NOT +# use multiple '_upvar' calls, since one '_upvar' call might +# reassign a variable to be used by another '_upvar' call. +# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference +_upvar() { + if unset -v "$1"; then # Unset & validate varname + if (( $# == 2 )); then + eval $1=\"\$2\" # Return single value + else + eval $1=\(\"\${@:2}\"\) # Return array + fi + fi +} + + +# Assign variables one scope above the caller +# Usage: local varname [varname ...] && +# _upvars [-v varname value] | [-aN varname [value ...]] ... +# Available OPTIONS: +# -aN Assign next N values to varname as array +# -v Assign single value to varname +# Return: 1 if error occurs +_upvars() { + if ! (( $# )); then + echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\ + "value] | [-aN varname [value ...]] ..." 1>&2 + return 2 + fi + while (( $# )); do + case $1 in + -a*) + # Error checking + [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\ + "number specifier" 1>&2; return 1; } + printf %d "${1#-a}" &> /dev/null || { echo "bash:"\ + "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2 + return 1; } + # Assign array of -aN elements + [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) && + shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\ + "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; } + ;; + -v) + # Assign single value + [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" && + shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\ + "argument(s)" 1>&2; return 1; } + ;; + --help) echo "\ +Usage: local varname [varname ...] && + ${FUNCNAME[0]} [-v varname value] | [-aN varname [value ...]] ... +Available OPTIONS: +-aN VARNAME [value ...] assign next N values to varname as array +-v VARNAME value assign single value to varname +--help display this help and exit +--version output version information and exit" + return 0 ;; + --version) echo "\ +${FUNCNAME[0]}-0.9.dev +Copyright (C) 2010 Freddy Vulto +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law." + return 0 ;; + *) + echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2 + return 1 ;; + esac + done +} + + # Reassemble command line words, excluding specified characters from the # list of word completion separators (COMP_WORDBREAKS). # @param $1 chars Characters out of $COMP_WORDBREAKS which should @@ -263,30 +340,10 @@ __reassemble_comp_words_by_ref() { # @param $4 cur Name of variable to return current word to complete to # @see ___get_cword_at_cursor_by_ref() __get_cword_at_cursor_by_ref() { - # NOTE: The call to the main function ___get_cword_at_cursor_by_ref() is - # wrapped to make collisions with local variable names less likely. - local __words __cword __cur - ___get_cword_at_cursor_by_ref "$1" __words __cword __cur - - eval $2=\( \"\${__words[@]}\" \) - eval $3=\$__cword - eval $4=\$__cur -} - - -# @param $1 exclude -# @param $2 words Name of variable to return words to -# @param $3 cword Name of variable to return cword to -# @param $4 cur Name of variable to return current word to complete to -# @note Do not call this function directly but call -# `__get_cword_at_cursor_by_ref()' instead to make variable name collisions -# less likely -# @see __get_cword_at_cursor_by_ref() -___get_cword_at_cursor_by_ref() { - local cword words + local cword words=() __reassemble_comp_words_by_ref "$1" words cword - local i + local i cur2 local cur="$COMP_LINE" local index="$COMP_POINT" for (( i = 0; i <= cword; ++i )); do @@ -314,13 +371,13 @@ ___get_cword_at_cursor_by_ref() { if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then # We messed up. At least return the whole word so things keep working - eval $4=\"\${words[cword]}\" + cur2=${words[cword]} else - eval $4=\"\${cur:0:\$index}\" + cur2=${cur:0:$index} fi - eval $2=\( \"\${words[@]}\" \) - eval $3=\$cword + local "$2" "$3" "$4" && + _upvars -a${#words[@]} $2 "${words[@]}" -v $3 "$cword" -v $4 "$cur2" } @@ -332,10 +389,10 @@ ___get_cword_at_cursor_by_ref() { # Also one is able to cross over possible wordbreak characters. # Usage: _get_comp_words_by_ref [OPTIONS] [VARNAMES] # Available VARNAMES: -# cur Return cur within varname "cur" -# prev Return prev within varname "prev" -# words Return words within varname "words" -# cword Return cword within varname "cword" +# cur Return cur via $cur +# prev Return prev via $prev +# words Return words via $words +# cword Return cword via $cword # # Available OPTIONS: # -n EXCLUDE Characters out of $COMP_WORDBREAKS which should NOT be @@ -344,81 +401,52 @@ ___get_cword_at_cursor_by_ref() { # would pass the colon (:) as -n option in this case. Bash-3 # doesn't do word splitting, so this ensures we get the same # word on both bash-3 and bash-4. -# -c VARNAME Return cur within specified VARNAME -# -p VARNAME Return prev within specified VARNAME -# -w VARNAME Return words within specified VARNAME -# -i VARNAME Return cword within specified VARNAME +# -c VARNAME Return cur via $VARNAME +# -p VARNAME Return prev via $VARNAME +# -w VARNAME Return words via $VARNAME +# -i VARNAME Return cword via $VARNAME # # Example usage: # # $ _get_comp_words_by_ref -n : cur prev # -# @see __get_comp_words_by_ref -_get_comp_words_by_ref() { - # NOTE: The call to the main function __get_comp_words_by_ref() is wrapped - # to make collisions with local variable names less likely. - local __words __cword __cur - local __var_cur __var_prev __var_words __var_cword - - __get_comp_words_by_ref \ - __words __cword __cur \ - __var_cur __var_prev __var_words __var_cword "$@" - - [[ $__var_cur ]] && eval $__var_cur=\$__cur - [[ $__var_prev ]] && ((__cword)) && eval $__var_prev=\${__words[__cword - 1]} - [[ $__var_words ]] && eval $__var_words=\${__words[@]} - [[ $__var_cword ]] && eval $__var_cword=\$__cword - return 0 -} - - -# @param $1 words Name of variable to return words to -# @param $2 cword Name of variable to return cword to -# @param $3 cur Name of variable to return current word to complete to -# @param $4 var_cur Name of variable to return current word to complete to -# @param $5 var_prev Name of variable to return previous word to complete to -# @param $6 var_words Name of variable to return words to complete to -# @param $7 var_cword Name of variable to return index of words to complete to -# @param $@ Arguments to _get_comp_words_by_ref() -# @note Do not call this function directly but call `_get_comp_words_by_ref()' -# instead to make variable name collisions less likely -# -# @see _get_comp_words_by_ref() -__get_comp_words_by_ref() +_get_comp_words_by_ref() { - local exclude flag i OPTIND=8 # Skip first seven arguments - local cword words cur - local var_cur var_cword var_prev var_words + local exclude flag i OPTIND=1 + local cur cword words=() + local upargs=() upvars=() vcur vcword vprev vwords while getopts "c:i:n:p:w:" flag "$@"; do case $flag in - c) var_cur=$OPTARG ;; - i) var_cword=$OPTARG ;; + c) vcur=$OPTARG ;; + i) vcword=$OPTARG ;; n) exclude=$OPTARG ;; - p) var_prev=$OPTARG ;; - w) var_words=$OPTARG ;; + p) vprev=$OPTARG ;; + w) vwords=$OPTARG ;; esac done while [[ $# -ge $OPTIND ]]; do case ${!OPTIND} in - cur) var_cur=cur ;; - prev) var_prev=prev ;; - cword) var_cword=cword ;; - words) var_words=words ;; - *) echo "error: $FUNCNAME(): unknown argument: ${!OPTIND}" + cur) vcur=cur ;; + prev) vprev=prev ;; + cword) vcword=cword ;; + words) vwords=words ;; + *) echo "bash: $FUNCNAME(): \`${!OPTIND}': unknown argument" \ + 1>&2; return 1 esac let "OPTIND += 1" done __get_cword_at_cursor_by_ref "$exclude" words cword cur - eval $1=\( \"\${words[@]}\" \) - eval $2=\$cword - eval $3=\$cur - eval $4=\$var_cur - eval $5=\$var_prev - eval $6=\${var_words[@]} - eval $7=\$var_cword + [[ $vcur ]] && { upvars+=("$vcur" ); upargs+=(-v $vcur "$cur" ); } + [[ $vcword ]] && { upvars+=("$vcword"); upargs+=(-v $vcword "$cword"); } + [[ $vprev ]] && { upvars+=("$vprev" ); upargs+=(-v $vprev + "${words[cword - 1]}"); } + [[ $vwords ]] && { upvars+=("$vwords"); upargs+=(-a${#words[@]} $vwords + "${words[@]}"); } + + (( ${#upvars[@]} )) && local "${upvars[@]}" && _upvars "${upargs[@]}" } @@ -436,7 +464,8 @@ __get_comp_words_by_ref() # current word (default is 0, previous is 1), respecting the exclusions # given at $1. For example, `_get_cword "=:" 1' returns the word left of # the current word, respecting the exclusions "=:". -# +# @deprecated Use `_get_comp_words_by_ref cur' instead +# @see _get_comp_words_by_ref() _get_cword() { local cword words @@ -489,7 +518,8 @@ _get_cword() # This is a good alternative to `prev=${COMP_WORDS[COMP_CWORD-1]}' because bash4 # will properly return the previous word with respect to any given exclusions to # COMP_WORDBREAKS. -# @see _get_cword() +# @deprecated Use `_get_comp_words_by_ref cur prev' instead +# @see _get_comp_words_by_ref() # _get_pword() { @@ -797,7 +827,7 @@ __expand_tilde_by_ref() { # becomes "~a". Double quotes allow eval. # 2: Remove * before the first slash (/), i.e. "~a/b" # becomes "b". Single quotes prevent eval. - # +-----1----+ +---2----+ + # +-----1----+ +---2----+ eval $1="${!1/%\/*}"/'${!1#*/}' else # No, $1 doesn't contain slash @@ -1593,7 +1623,7 @@ complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 ping \ # _cd() { - local IFS=$'\t\n' i j k + local cur IFS=$'\t\n' i j k _get_comp_words_by_ref cur # try to allow variable completion diff --git a/contrib/screen b/contrib/screen index a0df1227..721c076c 100644 --- a/contrib/screen +++ b/contrib/screen @@ -16,12 +16,12 @@ _screen_sessions() } && _screen() { - local cur prev preprev + local cur prev words cword COMPREPLY=() - _get_comp_words_by_ref cur prev preprev + _get_comp_words_by_ref cur prev words cword - case $preprev in + case ${words[cwords-2]} in -[dD]) _screen_sessions return 0 diff --git a/test/unit/_get_comp_words_by_ref.exp b/test/unit/_get_comp_words_by_ref.exp index 1016d997..22561e7f 100644 --- a/test/unit/_get_comp_words_by_ref.exp +++ b/test/unit/_get_comp_words_by_ref.exp @@ -341,7 +341,7 @@ sync_after_int set test {unknown argument should raise error} set cmd {_get_comp_words_by_ref dummy} -assert_bash_list {"error: __get_comp_words_by_ref(): unknown argument: dummy"} $cmd $test +assert_bash_list {"bash: _get_comp_words_by_ref(): `dummy': unknown argument"} $cmd $test sync_after_int |