[ovs-dev] [RFC] ovs-vsctl-bashcomp: Avoid setting the COMP_WORDBREAKS.

Alex Wang alexw at nicira.com
Tue Mar 17 18:32:30 UTC 2015


Modifying $COMP_WORDBREAKS in completion script is not the recommended
as it is a global variable and the modification could affect the behavior
of other completion scripts.  As a workaround, this commit uses the
_get_comp_words_by_ref which allows user to exclude characters out of
$COMP_WORDBREAKS and reassemble input command line.  However, as a side
effect, the bash completion module cannot handle characters defined in
$COMP_WORDBREAKS (e.g. ':' and '=') correctly in the resulting completions.
Thusly, we need to trim the colon-word and equal-word prefixes from reply.
This will reduce the functionality of some fancy completions.  For example,
COLUMN?:KEY=VALUE will not complete on KEY and VALUE.

Signed-off-by: Alex Wang <alexw at nicira.com>
---
 tests/completion.at               |   14 +++++------
 utilities/ovs-vsctl-bashcomp.bash |   49 ++++++++++++++++++++++++++++++++++---
 2 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/tests/completion.at b/tests/completion.at
index d3b2800..7006260 100644
--- a/tests/completion.at
+++ b/tests/completion.at
@@ -608,7 +608,7 @@ AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config ran
 [0], [dnl
 ${MATCH}
 ])
-MATCH="$(PREPARE_MATCH_NOSPACE(random_key=abc))"
+MATCH="$(PREPARE_MATCH_NOSPACE(abc))"
 AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key=abc"],
 [0], [dnl
 ${MATCH}
@@ -638,14 +638,14 @@ AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other"],
 ${MATCH}
 ])
 # then, with the ':' we should complete on key.
-TMP="$(ovs-vsctl --no-heading --columns=other_config list Bridge br0 | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 | xargs printf "other_config:%s\n")"
+TMP="$(ovs-vsctl --no-heading --columns=other_config list Bridge br0 | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1)"
 MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))"
 AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:"],
 [0], [dnl
 ${MATCH}
 ])
 # finally, if user fill in some value, we should just complete on user input.
-MATCH="$(PREPARE_MATCH_NOSPACE(other_config:random_val1=12345))"
+MATCH="$(PREPARE_MATCH_NOSPACE(12345))"
 AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_val1=12345"],
 [0], [dnl
 ${MATCH}
@@ -677,17 +677,15 @@ AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager "],
 ${MATCH}
 ])
 # filename completion on unix, punix.
-MATCH="$(PREPARE_MATCH_NOSPACE(unix:testsuite.log))"
+MATCH="$(PREPARE_MATCH_NOSPACE(testsuite.log))"
 AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager unix:test"],
 [0], [dnl
 ${MATCH}
 ])
-# no completion on other type, just return available types.
-# in real environment, bash will not complete on anything.
-MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: ssl: tcp: unix:))"
+# no completion on other type.
 AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager ssl:something"],
 [0], [dnl
-${MATCH}
+
 ])
 
 
diff --git a/utilities/ovs-vsctl-bashcomp.bash b/utilities/ovs-vsctl-bashcomp.bash
index e79c6a2..a2b56ad 100755
--- a/utilities/ovs-vsctl-bashcomp.bash
+++ b/utilities/ovs-vsctl-bashcomp.bash
@@ -604,6 +604,36 @@ _ovs_vsctl_process_messages () {
     fi
 }
 
+# colon, equal sign will mess up the completion output, just
+# removes the colon-word and equal-word prefix from COMPREPLY items.
+#
+# Implementation of this function refers to the __ltrim_colon_completions
+# function defined in bash_completion module.
+_ovs_vsctl_trim_compreply() {
+    local cur=$1
+
+    if [[
+        "$cur" == *:* && "$COMP_WORDBREAKS" == *:*
+    ]]; then
+        local colon_word=${cur%${cur##*:}}
+        local i=${#COMPREPLY[*]}
+        cur=${cur##*:}
+        while [ $((--i)) -ge 0 ]; do
+            COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
+        done
+    fi
+
+    if [[
+        "$cur" == *=* && "$COMP_WORDBREAKS" == *=*
+    ]]; then
+        local equal_word=${cur%${cur##*=}}
+        local i=${#COMPREPLY[*]}
+        while [ $((--i)) -ge 0 ]; do
+            COMPREPLY[$i]=${COMPREPLY[$i]#"$equal_word"}
+        done
+    fi
+}
+
 # The general strategy here is that the same functions that decide
 # completions can also capture the necessary context for later
 # completions.  This means that there is no distinction between the
@@ -627,7 +657,7 @@ _ovs_vsctl_process_messages () {
 # status of the function _ovs_vsctl_complete_argument represents where
 # it has determined that the next argument will be.
 _ovs_vsctl_bashcomp () {
-    local cur valid_globals cmd_args raw_cmd cmd_pos valid_globals valid_opts
+    local words cword valid_globals cmd_args raw_cmd cmd_pos valid_globals valid_opts
     local test="false"
 
     # Does not support BASH_VERSION < 4.0
@@ -645,6 +675,12 @@ _ovs_vsctl_bashcomp () {
                   <<< "$tmp"
         export COMP_WORDS
         export COMP_CWORD="$((${#COMP_WORDS[@]}-1))"
+    else
+        # If not in test mode, reassembles the COMP_WORDS and COMP_CWORD
+        # using just space as word break.
+        _get_comp_words_by_ref -n "\"'><=;|&(:" -w words -i cword
+        COMP_WORDS=( "${words[@]}")
+        COMP_CWORD=${cword}
     fi
 
     # Extract the conf.db path.
@@ -654,20 +690,18 @@ _ovs_vsctl_bashcomp () {
     fi
 
     # If having trouble accessing the database, return.
-    if ! _ovs_vsctl get-manager 2>/dev/null; then
+    if ! _ovs_vsctl get-manager 1>/dev/null 2>/dev/null; then
         return 1;
     fi
 
     _OVS_VSCTL_PARSED_ARGS=()
     _OVS_VSCTL_NEW_RECORDS=()
     cmd_pos=-1
-    cur=${COMP_WORDS[COMP_CWORD]}
     valid_globals=true
     valid_opts=true
     valid_commands=true
     given_opts=""
     index=1
-    export COMP_WORDBREAKS=" "
     for word in "${COMP_WORDS[@]:1:${COMP_CWORD}} "; do
         _OVS_VSCTL_COMP_NOSPACE=false
         local completion
@@ -753,6 +787,12 @@ _ovs_vsctl_bashcomp () {
         completion="$(sort -u <<< "$(tr ' ' '\n' <<< ${completion})")"
         if [ $index -eq $COMP_CWORD ]; then
             if [ "$test" = "true" ]; then
+                # Simulates the _ovs_vsctl_trim_compreply.
+                if [[
+                     "$word" == *:* || "$word" == *=*
+                   ]]; then
+                    completion="$(sed -e 's/.*://; s/.*=//' <<< "${completion}")"
+                fi
                 if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then
                     printf "%s" "$completion" | sed -e '/^$/d'
                 else
@@ -767,6 +807,7 @@ _ovs_vsctl_bashcomp () {
                     compopt +o nospace
                     COMPREPLY=( $(compgen -W "${completion}" -- $word) )
                 fi
+                _ovs_vsctl_trim_compreply "$word"
             fi
         fi
         index=$(($index+1))
-- 
1.7.9.5




More information about the dev mailing list