From fd32f09af19efc9b1279c54e0d8ed23f66232a15 Mon Sep 17 00:00:00 2001
From: John McCabe <johnmccabe@users.noreply.github.com>
Date: Thu, 8 Feb 2018 21:34:46 +0000
Subject: [PATCH] Fix generated bash completion for Bash 3 (OSX) (#520)

* Make preamble functions unique to command

Prior to this commit the functions in the preamble had names that didn't
vary based on the command for which the bash completion was generated.

This meant that if you had two bash completions with differences in the
preamble functions then only the last loaded function would be
available.

This commit prefixes all of these functions with the name of the command
so that multiple cobra generated completion files won't clash.

Signed-off-by: John McCabe <john@johnmccabe.net>

* Fix function names in writeFlagHandler

The references to the `__handle_filename_extension_flag` and
`__handle_subdirs_in_dir_flag` functions in `writeFlagHandler` hadn't
been updated correctly in the previous commits.

Signed-off-by: John McCabe <john@johnmccabe.net>

* Pass cmd into writeFlagHandler

This commit passes the cmd pointer into the writeFlagHandler so that the
`__handle_filename_extension_flag` and `__handle_subdirs_in_dir_flag`
functions can have the `cmd.Name()` prefixed.

* Update Bash completion tests

Prefixes the tested `__handle_filename_extension_flag` and
`__handle_subdirs_in_dir_flag` functions with the command name.
---
 bash_completions.go      | 98 ++++++++++++++++++++--------------------
 bash_completions_test.go |  5 +-
 2 files changed, 52 insertions(+), 51 deletions(-)

diff --git a/bash_completions.go b/bash_completions.go
index 2076b45..1d955ac 100644
--- a/bash_completions.go
+++ b/bash_completions.go
@@ -21,8 +21,8 @@ const (
 
 func writePreamble(buf *bytes.Buffer, name string) {
 	buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
-	buf.WriteString(`
-__debug()
+	buf.WriteString(fmt.Sprintf(`
+__%[1]s_debug()
 {
     if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
         echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
@@ -31,13 +31,13 @@ __debug()
 
 # Homebrew on Macs have version 1.3 of bash-completion which doesn't include
 # _init_completion. This is a very minimal version of that function.
-__my_init_completion()
+__%[1]s_init_completion()
 {
     COMPREPLY=()
     _get_comp_words_by_ref "$@" cur prev words cword
 }
 
-__index_of_word()
+__%[1]s_index_of_word()
 {
     local w word=$1
     shift
@@ -49,7 +49,7 @@ __index_of_word()
     index=-1
 }
 
-__contains_word()
+__%[1]s_contains_word()
 {
     local w word=$1; shift
     for w in "$@"; do
@@ -58,9 +58,9 @@ __contains_word()
     return 1
 }
 
-__handle_reply()
+__%[1]s_handle_reply()
 {
-    __debug "${FUNCNAME[0]}"
+    __%[1]s_debug "${FUNCNAME[0]}"
     case $cur in
         -*)
             if [[ $(type -t compopt) = "builtin" ]]; then
@@ -85,7 +85,7 @@ __handle_reply()
 
                 local index flag
                 flag="${cur%%=*}"
-                __index_of_word "${flag}" "${flags_with_completion[@]}"
+                __%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}"
                 COMPREPLY=()
                 if [[ ${index} -ge 0 ]]; then
                     PREFIX=""
@@ -103,7 +103,7 @@ __handle_reply()
 
     # check if we are handling a flag with special work handling
     local index
-    __index_of_word "${prev}" "${flags_with_completion[@]}"
+    __%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}"
     if [[ ${index} -ge 0 ]]; then
         ${flags_completion[${index}]}
         return
@@ -139,21 +139,21 @@ __handle_reply()
 }
 
 # The arguments should be in the form "ext1|ext2|extn"
-__handle_filename_extension_flag()
+__%[1]s_handle_filename_extension_flag()
 {
     local ext="$1"
     _filedir "@(${ext})"
 }
 
-__handle_subdirs_in_dir_flag()
+__%[1]s_handle_subdirs_in_dir_flag()
 {
     local dir="$1"
     pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
 }
 
-__handle_flag()
+__%[1]s_handle_flag()
 {
-    __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
+    __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
 
     # if a command required a flag, and we found it, unset must_have_one_flag()
     local flagname=${words[c]}
@@ -164,13 +164,13 @@ __handle_flag()
         flagname=${flagname%%=*} # strip everything after the =
         flagname="${flagname}=" # but put the = back
     fi
-    __debug "${FUNCNAME[0]}: looking for ${flagname}"
-    if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
+    __%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}"
+    if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then
         must_have_one_flag=()
     fi
 
     # if you set a flag which only applies to this command, don't show subcommands
-    if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
+    if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
       commands=()
     fi
 
@@ -187,7 +187,7 @@ __handle_flag()
     fi
 
     # skip the argument to a two word flag
-    if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
+    if __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then
         c=$((c+1))
         # if we are looking for a flags value, don't show commands
         if [[ $c -eq $cword ]]; then
@@ -199,13 +199,13 @@ __handle_flag()
 
 }
 
-__handle_noun()
+__%[1]s_handle_noun()
 {
-    __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
+    __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
 
-    if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
+    if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
         must_have_one_noun=()
-    elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then
+    elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then
         must_have_one_noun=()
     fi
 
@@ -213,9 +213,9 @@ __handle_noun()
     c=$((c+1))
 }
 
-__handle_command()
+__%[1]s_handle_command()
 {
-    __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
+    __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
 
     local next_command
     if [[ -n ${last_command} ]]; then
@@ -228,30 +228,30 @@ __handle_command()
         fi
     fi
     c=$((c+1))
-    __debug "${FUNCNAME[0]}: looking for ${next_command}"
+    __%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}"
     declare -F "$next_command" >/dev/null && $next_command
 }
 
-__handle_word()
+__%[1]s_handle_word()
 {
     if [[ $c -ge $cword ]]; then
-        __handle_reply
+        __%[1]s_handle_reply
         return
     fi
-    __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
+    __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
     if [[ "${words[c]}" == -* ]]; then
-        __handle_flag
-    elif __contains_word "${words[c]}" "${commands[@]}"; then
-        __handle_command
-    elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
-        __handle_command
+        __%[1]s_handle_flag
+    elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then
+        __%[1]s_handle_command
+    elif [[ $c -eq 0 ]] && __%[1]s_contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
+        __%[1]s_handle_command
     else
-        __handle_noun
+        __%[1]s_handle_noun
     fi
-    __handle_word
+    __%[1]s_handle_word
 }
 
-`)
+`, name))
 }
 
 func writePostscript(buf *bytes.Buffer, name string) {
@@ -263,7 +263,7 @@ func writePostscript(buf *bytes.Buffer, name string) {
     if declare -F _init_completion >/dev/null 2>&1; then
         _init_completion -s || return
     else
-        __my_init_completion -n "=" || return
+        __%[1]s_init_completion -n "=" || return
     fi
 
     local c=0
@@ -272,13 +272,13 @@ func writePostscript(buf *bytes.Buffer, name string) {
     local local_nonpersistent_flags=()
     local flags_with_completion=()
     local flags_completion=()
-    local commands=("%s")
+    local commands=("%[1]s")
     local must_have_one_flag=()
     local must_have_one_noun=()
     local last_command
     local nouns=()
 
-    __handle_word
+    __%[1]s_handle_word
 }
 
 `, name))
@@ -303,7 +303,7 @@ func writeCommands(buf *bytes.Buffer, cmd *Command) {
 	buf.WriteString("\n")
 }
 
-func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string) {
+func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) {
 	for key, value := range annotations {
 		switch key {
 		case BashCompFilenameExt:
@@ -311,7 +311,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
 
 			var ext string
 			if len(value) > 0 {
-				ext = "__handle_filename_extension_flag " + strings.Join(value, "|")
+				ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Name()) + strings.Join(value, "|")
 			} else {
 				ext = "_filedir"
 			}
@@ -329,7 +329,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
 
 			var ext string
 			if len(value) == 1 {
-				ext = "__handle_subdirs_in_dir_flag " + value[0]
+				ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Name()) + value[0]
 			} else {
 				ext = "_filedir -d"
 			}
@@ -338,7 +338,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
 	}
 }
 
-func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
+func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
 	name := flag.Shorthand
 	format := "    "
 	if len(flag.NoOptDefVal) == 0 {
@@ -346,10 +346,10 @@ func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
 	}
 	format += "flags+=(\"-%s\")\n"
 	buf.WriteString(fmt.Sprintf(format, name))
-	writeFlagHandler(buf, "-"+name, flag.Annotations)
+	writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
 }
 
-func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
+func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
 	name := flag.Name
 	format := "    flags+=(\"--%s"
 	if len(flag.NoOptDefVal) == 0 {
@@ -357,7 +357,7 @@ func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
 	}
 	format += "\")\n"
 	buf.WriteString(fmt.Sprintf(format, name))
-	writeFlagHandler(buf, "--"+name, flag.Annotations)
+	writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
 }
 
 func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
@@ -383,9 +383,9 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
 		if nonCompletableFlag(flag) {
 			return
 		}
-		writeFlag(buf, flag)
+		writeFlag(buf, flag, cmd)
 		if len(flag.Shorthand) > 0 {
-			writeShortFlag(buf, flag)
+			writeShortFlag(buf, flag, cmd)
 		}
 		if localNonPersistentFlags.Lookup(flag.Name) != nil {
 			writeLocalNonPersistentFlag(buf, flag)
@@ -395,9 +395,9 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
 		if nonCompletableFlag(flag) {
 			return
 		}
-		writeFlag(buf, flag)
+		writeFlag(buf, flag, cmd)
 		if len(flag.Shorthand) > 0 {
-			writeShortFlag(buf, flag)
+			writeShortFlag(buf, flag, cmd)
 		}
 	})
 
diff --git a/bash_completions_test.go b/bash_completions_test.go
index a0da871..dd3a88e 100644
--- a/bash_completions_test.go
+++ b/bash_completions_test.go
@@ -2,6 +2,7 @@ package cobra
 
 import (
 	"bytes"
+	"fmt"
 	"os"
 	"os/exec"
 	"strings"
@@ -146,11 +147,11 @@ func TestBashCompletions(t *testing.T) {
 	// check for filename extension flags
 	check(t, output, `must_have_one_noun+=("three")`)
 	// check for filename extension flags
-	check(t, output, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
+	check(t, output, fmt.Sprintf(`flags_completion+=("__%s_handle_filename_extension_flag json|yaml|yml")`, rootCmd.Name()))
 	// check for custom flags
 	check(t, output, `flags_completion+=("__complete_custom")`)
 	// check for subdirs_in_dir flags
-	check(t, output, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`)
+	check(t, output, fmt.Sprintf(`flags_completion+=("__%s_handle_subdirs_in_dir_flag themes")`, rootCmd.Name()))
 
 	checkOmit(t, output, deprecatedCmd.Name())