Repository: spf13/cobra
Pull request #730 (2018-08-20 13:25:35)

Qualify custom bash func name
 - fixes issue where multiple cobra apps using custom bash completion
 would have their __custom_func collide
 - support fallback to plain __custom_func to maintain compatibility

#694 

I tested this with a cobra app I'm developing locally and had success.  I'm not totally sure how write automated tests for it other than the changes I made.

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/spf13/cobra?pullRequest=730) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/spf13/cobra?pullRequest=730) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/spf13/cobra?pullRequest=730) it.</sub>
eparis: I think the best way to test would be to define a custom func and then just do a string search to see if it shows up twice in the output...  (Once for the actual func def, and once for the call to it?)

Also need the CLA.

Patch:
diff --git a/bash_completions.go b/bash_completions.go
index 8fa8f48..63274d9 100644
--- a/bash_completions.go
+++ b/bash_completions.go
@@ -129,7 +129,13 @@ __%[1]s_handle_reply()
     fi
 
     if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
-        declare -F __custom_func >/dev/null && __custom_func
+		if declare -F __%[1]s_custom_func >/dev/null; then
+			# try command name qualified custom func
+			__%[1]s_custom_func
+		else
+			# otherwise fall back to unqualified for compatibility
+			declare -F ___custom_func >/dev/null && __custom_func
+		fi
     fi
 
     # available in bash-completion >= 2, not always present on macOS
diff --git a/bash_completions.md b/bash_completions.md
index 49d0680..3bd32e0 100644
--- a/bash_completions.md
+++ b/bash_completions.md
@@ -82,7 +82,7 @@ __kubectl_get_resource()
     fi
 }
 
-__custom_func() {
+__kubectl_custom_func() {
     case ${last_command} in
         kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop)
             __kubectl_get_resource
@@ -109,7 +109,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
 }
 ```
 
-The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__custom_func()` to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`.  `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`.  So it will call `__kubectl_parse_get pod`.  `__kubectl_parse_get` will actually call out to kubernetes and get any pods.  It will then set `COMPREPLY` to valid pods!
+The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `___kubectl_custom_func()` (`__<command-use>_custom_func()`) to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`.  `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`.  So it will call `__kubectl_parse_get pod`.  `__kubectl_parse_get` will actually call out to kubernetes and get any pods.  It will then set `COMPREPLY` to valid pods!
 
 ## Have the completions code complete your 'nouns'
 
diff --git a/doc/man_docs.go b/doc/man_docs.go
index baa4811..e5dbda2 100644
--- a/doc/man_docs.go
+++ b/doc/man_docs.go
@@ -87,7 +87,7 @@ type GenManTreeOptions struct {
 
 // GenManHeader is a lot like the .TH header at the start of man pages. These
 // include the title, section, date, source, and manual. We will use the
-// current time if Date if unset and will use "Auto generated by spf13/cobra"
+// current time if Date is unset and will use "Auto generated by spf13/cobra"
 // if the Source is unset.
 type GenManHeader struct {
 	Title   string


Test patch:
diff --git a/bash_completions_test.go b/bash_completions_test.go
index 02a4f15..94b965d 100644
--- a/bash_completions_test.go
+++ b/bash_completions_test.go
@@ -22,6 +22,13 @@ func check(t *testing.T, found, expected string) {
 	}
 }
 
+func checkNumOccurrences(t *testing.T, found, expected string, expectedOccurrences int) {
+	numOccurrences := strings.Count(found, expected)
+	if numOccurrences != expectedOccurrences {
+		t.Errorf("Expecting to contain %d occurrences of: \n %q\nGot %d:\n %q\n", expectedOccurrences, expected, numOccurrences, found)
+	}
+}
+
 func checkRegex(t *testing.T, found, pattern string) {
 	matched, err := regexp.MatchString(pattern, found)
 	if err != nil {
@@ -53,7 +60,7 @@ func runShellCheck(s string) error {
 }
 
 // World worst custom function, just keep telling you to enter hello!
-const bashCompletionFunc = `__custom_func() {
+const bashCompletionFunc = `__root_custom_func() {
 	COMPREPLY=( "hello" )
 }
 `
@@ -150,7 +157,10 @@ func TestBashCompletions(t *testing.T) {
 	// check for required flags
 	check(t, output, `must_have_one_flag+=("--introot=")`)
 	check(t, output, `must_have_one_flag+=("--persistent-filename=")`)
-	// check for custom completion function
+	// check for custom completion function with both qualified and unqualified name
+	checkNumOccurrences(t, output, `__custom_func`, 2)      // 1. check existence, 2. invoke
+	checkNumOccurrences(t, output, `__root_custom_func`, 3) // 1. check existence, 2. invoke, 3. actual definition
+	// check for custom completion function body
 	check(t, output, `COMPREPLY=( "hello" )`)
 	// check for required nouns
 	check(t, output, `must_have_one_noun+=("pod")`)