[Bash-completion-commits] [SCM] bash-completion branch, use-_get_comp_words_by_ref, updated. a8dd58cfa9a9e409053628c62f36880928b3e1c6

Freddy Vulto fvulto at gmail.com
Wed Jun 9 20:37:22 UTC 2010


The following commit has been merged in the use-_get_comp_words_by_ref branch:
commit a8dd58cfa9a9e409053628c62f36880928b3e1c6
Author: Freddy Vulto <fvulto at gmail.com>
Date:   Wed Jun 9 22:37:02 2010 +0200

    Added `_upvars' and `_upvar'.
    These helper methods aid in passing variables by reference.

diff --git a/bash_completion b/bash_completion
index 0d329f8..e427bd2 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 a0df122..721c076 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 1016d99..22561e7 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

-- 
bash-completion



More information about the Bash-completion-commits mailing list