Repository: spf13/cobra
Pull request #1034 (2020-02-13 14:01:53)

Add Github CLI to list of projects using Cobra

claassistantio: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/spf13/cobra?pullRequest=1034) <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=1034) 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=1034) it.</sub>

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 8194464..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-version: 2
-
-references:
-  workspace: &workspace
-    /go/src/github.com/spf13/cobra
-
-  run_tests: &run_tests
-    run:
-      name: "All Commands"
-      command: |
-        mkdir -p bin
-        curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.6/shellcheck
-        chmod +x bin/shellcheck
-        go get -t -v ./...
-        PATH=$PATH:$PWD/bin go test -v ./...
-        go build
-        if [ -z $NOVET ]; then
-          diff -u <(echo -n) <(go vet . 2>&1 | grep -vE 'ExampleCommand|bash_completions.*Fprint');
-        fi
-
-jobs:
-  go-current:
-    docker:
-      - image: circleci/golang:1.12
-    working_directory: *workspace
-    steps:
-      - checkout
-      - *run_tests
-      - run:
-          name: "Check formatting"
-          command: diff -u <(echo -n) <(gofmt -d -s .)
-  go-previous:
-    docker:
-      - image: circleci/golang:1.11
-    working_directory: *workspace
-    steps:
-      - checkout
-      - *run_tests
-  go-latest:
-    docker:
-      - image: circleci/golang:latest
-    working_directory: *workspace
-    steps:
-      - checkout
-      - *run_tests
-
-workflows:
-  version: 2
-  main:
-    jobs:
-      - go-current
-      - go-previous
-      - go-latest
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 0000000..a4982bf
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,12 @@
+# changes to documentation
+"area/documentation": doc/**/*
+
+# changes to the core lib package
+"area/lib": ./*.go
+
+# changes to the zsh completion
+"area/*sh completion":
+  - ./zsh_*
+  - ./shell_*
+  - ./powershell_*
+  - ./bash_*
diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml
new file mode 100644
index 0000000..e90b599
--- /dev/null
+++ b/.github/workflows/label.yml
@@ -0,0 +1,19 @@
+# This workflow will triage pull requests and apply a label based on the
+# paths that are modified in the pull request.
+#
+# To use this workflow, you will need to set up a .github/labeler.yml
+# file with configuration.  For more information, see:
+# https://github.com/actions/labeler/blob/master/README.md
+
+name: Labeler
+on: [pull_request]
+
+jobs:
+  label:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/labeler@v2
+      with:
+        repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000..890ed01
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,21 @@
+name: Mark stale issues and pull requests
+
+on:
+  schedule:
+  - cron: "0 0 * * *"
+
+jobs:
+  stale:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/stale@v1
+      with:
+        repo-token: ${{ secrets.GITHUB_TOKEN }}
+        stale-issue-message: 'This issue is being marked as stale due to a long period of inactivity'
+        stale-pr-message: 'This PR is being marked as stale due to a long period of inactivity'
+        stale-issue-label: 'kind/stale'
+        stale-pr-label: 'kind/stale'
+        exempt-issue-label: 'kind/stale'
+        exempt-pr-label: 'kind/stale'
diff --git a/.gitignore b/.gitignore
index b2b848e..c7b459e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,8 +32,8 @@ Session.vim
 tags
 
 *.exe
-cobra
 cobra.test
+bin
 
 .idea/
 *.iml
diff --git a/.travis.yml b/.travis.yml
index fca1e69..a9bd4e5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,26 +3,27 @@ language: go
 stages:
   - diff
   - test
+  - build
 
 go:
-  - 1.10.x
-  - 1.11.x
   - 1.12.x
+  - 1.13.x
   - tip
 
+before_install:
+  - go get -u github.com/kyoh86/richgo
+  - go get -u github.com/mitchellh/gox
+
 matrix:
   allow_failures:
     - go: tip
   include:
     - stage: diff
-      go: 1.12.x
-      script: diff -u <(echo -n) <(gofmt -d -s .)
-
-before_install: go get -u github.com/kyoh86/richgo
+      go: 1.13.x
+      script: make fmt
+    - stage: build
+      go: 1.13.x
+      script: make cobra_generator
 
-script:
-  - richgo test -v ./...
-  - go build
-  - if [ -z $NOVET ]; then
-      diff -u <(echo -n) <(go vet . 2>&1 | grep -vE 'ExampleCommand|bash_completions.*Fprint');
-    fi
+script: 
+ - make test
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e9740d1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+BIN="./bin"
+SRC=$(shell find . -name "*.go")
+
+ifeq (, $(shell which richgo))
+$(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo")
+endif
+
+.PHONY: fmt vet test cobra_generator install_deps clean
+
+default: all
+
+all: fmt vet test cobra_generator	
+
+fmt:
+	$(info ******************** checking formatting ********************)
+	@test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1)
+
+test: install_deps vet
+	$(info ******************** running tests ********************)
+	richgo test -v ./...
+
+cobra_generator: install_deps
+	$(info ******************** building generator ********************)
+	mkdir -p $(BIN)
+	make -C cobra all
+
+install_deps:
+	$(info ******************** downloading dependencies ********************)
+	go get -v ./...
+
+vet:
+	$(info ******************** vetting ********************)
+	go vet ./...
+
+clean:
+	rm -rf $(BIN)
diff --git a/README.md b/README.md
index 834f287..9d79934 100644
--- a/README.md
+++ b/README.md
@@ -25,10 +25,10 @@ Many of the most widely used Go projects are built using Cobra, such as:
 [mattermost-server](https://github.com/mattermost/mattermost-server),
 [Gardener](https://github.com/gardener/gardenctl),
 [Linkerd](https://linkerd.io/),
+[Github CLI](https://github.com/cli/cli)
 etc.
 
 [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra)
-[![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/spf13/cobra)
 [![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra)
 [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra)
 
@@ -214,6 +214,7 @@ package cmd
 
 import (
 	"fmt"
+	"os"
 
 	homedir "github.com/mitchellh/go-homedir"
 	"github.com/spf13/cobra"
@@ -255,6 +256,11 @@ func init() {
 	rootCmd.AddCommand(initCmd)
 }
 
+func er(msg interface{}) {
+	fmt.Println("Error:", msg)
+	os.Exit(1)
+}
+
 func initConfig() {
 	if cfgFile != "" {
 		// Use config file from the flag.
diff --git a/bash_completions.go b/bash_completions.go
index 1e0e25c..b4098f4 100644
--- a/bash_completions.go
+++ b/bash_completions.go
@@ -58,6 +58,67 @@ __%[1]s_contains_word()
     return 1
 }
 
+__%[1]s_handle_go_custom_completion()
+{
+    __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}"
+
+    local out requestComp lastParam lastChar comp directive args
+
+    # Prepare the command to request completions for the program.
+    # Calling ${words[0]} instead of directly %[1]s allows to handle aliases
+    args=("${words[@]:1}")
+    requestComp="${words[0]} %[2]s ${args[*]}"
+
+    lastParam=${words[$((${#words[@]}-1))]}
+    lastChar=${lastParam:$((${#lastParam}-1)):1}
+    __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}"
+
+    if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then
+        # If the last parameter is complete (there is a space following it)
+        # We add an extra empty parameter so we can indicate this to the go method.
+        __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter"
+        requestComp="${requestComp} \"\""
+    fi
+
+    __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}"
+    # Use eval to handle any environment variables and such
+    out=$(eval "${requestComp}" 2>/dev/null)
+
+    # Extract the directive integer at the very end of the output following a colon (:)
+    directive=${out##*:}
+    # Remove the directive
+    out=${out%%:*}
+    if [ "${directive}" = "${out}" ]; then
+        # There is not directive specified
+        directive=0
+    fi
+    __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"
+    __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}"
+
+    if [ $((directive & %[3]d)) -ne 0 ]; then
+        # Error code.  No completion.
+        __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code"
+        return
+    else
+        if [ $((directive & %[4]d)) -ne 0 ]; then
+            if [[ $(type -t compopt) = "builtin" ]]; then
+                __%[1]s_debug "${FUNCNAME[0]}: activating no space"
+                compopt -o nospace
+            fi
+        fi
+        if [ $((directive & %[5]d)) -ne 0 ]; then
+            if [[ $(type -t compopt) = "builtin" ]]; then
+                __%[1]s_debug "${FUNCNAME[0]}: activating no file completion"
+                compopt +o default
+            fi
+        fi
+
+        while IFS='' read -r comp; do
+            COMPREPLY+=("$comp")
+        done < <(compgen -W "${out[*]}" -- "$cur")
+    fi
+}
+
 __%[1]s_handle_reply()
 {
     __%[1]s_debug "${FUNCNAME[0]}"
@@ -121,6 +182,10 @@ __%[1]s_handle_reply()
     completions=("${commands[@]}")
     if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
         completions=("${must_have_one_noun[@]}")
+    elif [[ -n "${has_completion_function}" ]]; then
+        # if a go completion function is provided, defer to that function
+        completions=()
+        __%[1]s_handle_go_custom_completion
     fi
     if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
         completions+=("${must_have_one_flag[@]}")
@@ -279,7 +344,7 @@ __%[1]s_handle_word()
     __%[1]s_handle_word
 }
 
-`, name))
+`, name, ShellCompRequestCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp))
 }
 
 func writePostscript(buf *bytes.Buffer, name string) {
@@ -304,6 +369,7 @@ func writePostscript(buf *bytes.Buffer, name string) {
     local commands=("%[1]s")
     local must_have_one_flag=()
     local must_have_one_noun=()
+    local has_completion_function
     local last_command
     local nouns=()
 
@@ -404,7 +470,22 @@ func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
 	buf.WriteString(fmt.Sprintf(format, name))
 }
 
+// Setup annotations for go completions for registered flags
+func prepareCustomAnnotationsForFlags(cmd *Command) {
+	for flag := range flagCompletionFunctions {
+		// Make sure the completion script calls the __*_go_custom_completion function for
+		// every registered flag.  We need to do this here (and not when the flag was registered
+		// for completion) so that we can know the root command name for the prefix
+		// of __<prefix>_go_custom_completion
+		if flag.Annotations == nil {
+			flag.Annotations = map[string][]string{}
+		}
+		flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())}
+	}
+}
+
 func writeFlags(buf *bytes.Buffer, cmd *Command) {
+	prepareCustomAnnotationsForFlags(cmd)
 	buf.WriteString(`    flags=()
     two_word_flags=()
     local_nonpersistent_flags=()
@@ -469,6 +550,9 @@ func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
 	for _, value := range cmd.ValidArgs {
 		buf.WriteString(fmt.Sprintf("    must_have_one_noun+=(%q)\n", value))
 	}
+	if cmd.ValidArgsFunction != nil {
+		buf.WriteString("    has_completion_function=1\n")
+	}
 }
 
 func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
diff --git a/bash_completions.md b/bash_completions.md
index 4ac61ee..e1590e1 100644
--- a/bash_completions.md
+++ b/bash_completions.md
@@ -56,7 +56,147 @@ func main() {
 
 `out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described [here](https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1) and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
 
-## Creating your own custom functions
+## Have the completions code complete your 'nouns'
+
+### Static completion of nouns
+
+This method allows you to provide a pre-defined list of completion choices for your nouns using the `validArgs` field.
+For example, if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like:
+
+```go
+validArgs []string = { "pod", "node", "service", "replicationcontroller" }
+
+cmd := &cobra.Command{
+	Use:     "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
+	Short:   "Display one or many resources",
+	Long:    get_long,
+	Example: get_example,
+	Run: func(cmd *cobra.Command, args []string) {
+		err := RunGet(f, out, cmd, args)
+		util.CheckErr(err)
+	},
+	ValidArgs: validArgs,
+}
+```
+
+Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
+
+```bash
+# kubectl get [tab][tab]
+node                 pod                    replicationcontroller  service
+```
+
+### Plural form and shortcuts for nouns
+
+If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
+
+```go
+argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
+
+cmd := &cobra.Command{
+    ...
+	ValidArgs:  validArgs,
+	ArgAliases: argAliases
+}
+```
+
+The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
+the completion algorithm if entered manually, e.g. in:
+
+```bash
+# kubectl get rc [tab][tab]
+backend        frontend       database 
+```
+
+Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns
+in this example again instead of the replication controllers.
+
+### Dynamic completion of nouns
+
+In some cases it is not possible to provide a list of possible completions in advance.  Instead, the list of completions must be determined at execution-time.  Cobra provides two ways of defining such dynamic completion of nouns. Note that both these methods can be used along-side each other as long as they are not both used for the same command.
+
+#### 1. Custom completions of nouns written in Go
+
+In a similar fashion as for static completions, you can use the `ValidArgsFunction` field to provide a Go function that Cobra will execute when it needs the list of completion choices for the nouns of a command.  Note that either `ValidArgs` or `ValidArgsFunction` can be used for a single cobra command, but not both.
+Simplified code from `helm status` looks like:
+
+```go
+cmd := &cobra.Command{
+	Use:   "status RELEASE_NAME",
+	Short: "Display the status of the named release",
+	Long:  status_long,
+	RunE: func(cmd *cobra.Command, args []string) {
+		RunGet(args[0])
+	},
+	ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+		if len(args) != 0 {
+			return nil, cobra.ShellCompDirectiveNoFileComp
+		}
+		return getReleasesFromCluster(toComplete), cobra.ShellCompDirectiveNoFileComp
+	},
+}
+```
+Where `getReleasesFromCluster()` is a Go function that obtains the list of current Helm releases running on the Kubernetes cluster.
+Notice we put the `ValidArgsFunction` on the `status` subcommand. Let's assume the Helm releases on the cluster are: `harbor`, `notary`, `rook` and `thanos` then this dynamic completion will give results like
+
+```bash
+# helm status [tab][tab]
+harbor notary rook thanos
+```
+You may have noticed the use of `cobra.ShellCompDirective`.  These directives are bit fields allowing to control some shell completion behaviors for your particular completion.  You can combine them with the bit-or operator such as `cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp`
+```go
+// Indicates an error occurred and completions should be ignored.
+ShellCompDirectiveError
+// Indicates that the shell should not add a space after the completion,
+// even if there is a single completion provided.
+ShellCompDirectiveNoSpace
+// Indicates that the shell should not provide file completion even when
+// no completion is provided.
+// This currently does not work for zsh or bash < 4
+ShellCompDirectiveNoFileComp
+// Indicates that the shell will perform its default behavior after completions
+// have been provided (this implies !ShellCompDirectiveNoSpace && !ShellCompDirectiveNoFileComp).
+ShellCompDirectiveDefault
+```
+
+When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line.  You therefore don't need to do this parsing yourself.  For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function.
+
+##### Debugging
+
+Cobra achieves dynamic completions written in Go through the use of a hidden command called by the completion script.  To debug your Go completion code, you can call this hidden command directly:
+```bash
+# helm __complete status har<ENTER>
+harbor
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
+```
+***Important:*** If the noun to complete is empty, you must pass an empty parameter to the `__complete` command:
+```bash
+# helm __complete status ""<ENTER>
+harbor
+notary
+rook
+thanos
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
+```
+Calling the `__complete` command directly allows you to run the Go debugger to troubleshoot your code.  You can also add printouts to your code; Cobra provides the following functions to use for printouts in Go completion code:
+```go
+// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE
+// is set to a file path) and optionally prints to stderr.
+cobra.CompDebug(msg string, printToStdErr bool) {
+cobra.CompDebugln(msg string, printToStdErr bool)
+
+// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE
+// is set to a file path) and to stderr.
+cobra.CompError(msg string)
+cobra.CompErrorln(msg string)
+```
+***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script.  Instead, use the cobra-provided debugging traces functions mentioned above.
+
+#### 2. Custom completions of nouns written in Bash
+
+This method allows you to inject bash functions into the completion script.  Those bash functions are responsible for providing the completion choices for your own completions.
 
 Some more actual code that works in kubernetes:
 
@@ -111,58 +251,6 @@ 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 `__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'
-
-In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like:
-
-```go
-validArgs []string = { "pod", "node", "service", "replicationcontroller" }
-
-cmd := &cobra.Command{
-	Use:     "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
-	Short:   "Display one or many resources",
-	Long:    get_long,
-	Example: get_example,
-	Run: func(cmd *cobra.Command, args []string) {
-		err := RunGet(f, out, cmd, args)
-		util.CheckErr(err)
-	},
-	ValidArgs: validArgs,
-}
-```
-
-Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
-
-```bash
-# kubectl get [tab][tab]
-node                 pod                    replicationcontroller  service
-```
-
-## Plural form and shortcuts for nouns
-
-If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
-
-```go
-argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
-
-cmd := &cobra.Command{
-    ...
-	ValidArgs:  validArgs,
-	ArgAliases: argAliases
-}
-```
-
-The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
-the completion algorithm if entered manually, e.g. in:
-
-```bash
-# kubectl get rc [tab][tab]
-backend        frontend       database 
-```
-
-Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns
-in this example again instead of the replication controllers.
-
 ## Mark flags as required
 
 Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types [tab][tab].  Marking a flag as 'Required' is incredibly easy.
@@ -211,8 +299,43 @@ So while there are many other files in the CWD it only shows me subdirs and thos
 
 # Specify custom flag completion
 
-Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specify
-a custom flag completion function with cobra.BashCompCustom:
+As for nouns, Cobra provides two ways of defining dynamic completion of flags.  Note that both these methods can be used along-side each other as long as they are not both used for the same flag.
+
+## 1. Custom completions of flags written in Go
+
+To provide a Go function that Cobra will execute when it needs the list of completion choices for a flag, you must register the function in the following manner:
+
+```go
+flagName := "output"
+cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+	return []string{"json", "table", "yaml"}, cobra.ShellCompDirectiveDefault
+})
+```
+Notice that calling `RegisterFlagCompletionFunc()` is done through the `command` with which the flag is associated.  In our example this dynamic completion will give results like so:
+
+```bash
+# helm status --output [tab][tab]
+json table yaml
+```
+
+### Debugging
+
+You can also easily debug your Go completion code for flags:
+```bash
+# helm __complete status --output ""
+json
+table
+yaml
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
+```
+***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script.  Instead, use the cobra-provided debugging traces functions mentioned in the above section.
+
+## 2. Custom completions of flags written in Bash
+
+Alternatively, you can use bash code for flag custom completion. Similar to the filename
+completion and filtering using `cobra.BashCompFilenameExt`, you can specify
+a custom flag completion bash function with `cobra.BashCompCustom`:
 
 ```go
 	annotation := make(map[string][]string)
@@ -226,7 +349,7 @@ a custom flag completion function with cobra.BashCompCustom:
 	cmd.Flags().AddFlag(flag)
 ```
 
-In addition add the `__handle_namespace_flag` implementation in the `BashCompletionFunction`
+In addition add the `__kubectl_get_namespaces` implementation in the `BashCompletionFunction`
 value, e.g.:
 
 ```bash
diff --git a/cobra/Makefile b/cobra/Makefile
new file mode 100644
index 0000000..4995000
--- /dev/null
+++ b/cobra/Makefile
@@ -0,0 +1,23 @@
+XC_OS="linux darwin"
+XC_ARCH="amd64"
+XC_PARALLEL="2"
+BIN="../bin"
+SRC=$(shell find . -name "*.go")
+
+ifeq (, $(shell which gox))
+$(warning "could not find gox in $(PATH), run: go get github.com/mitchellh/gox")
+endif
+
+.PHONY: all build
+
+default: all
+
+all: build
+
+build:
+	gox \
+		-os=$(XC_OS) \
+		-arch=$(XC_ARCH) \
+		-parallel=$(XC_PARALLEL) \
+		-output=$(BIN)/{{.Dir}}_{{.OS}}_{{.Arch}} \
+		;
diff --git a/command.go b/command.go
index ab3cf69..88e6ed7 100644
--- a/command.go
+++ b/command.go
@@ -17,7 +17,7 @@ package cobra
 
 import (
 	"bytes"
-	"errors"
+	"context"
 	"fmt"
 	"io"
 	"os"
@@ -28,8 +28,6 @@ import (
 	flag "github.com/spf13/pflag"
 )
 
-var ErrSubCommandRequired = errors.New("subcommand is required")
-
 // FParseErrWhitelist configures Flag parse errors to be ignored
 type FParseErrWhitelist flag.ParseErrorsWhitelist
 
@@ -59,6 +57,10 @@ type Command struct {
 
 	// ValidArgs is list of all valid non-flag arguments that are accepted in bash completions
 	ValidArgs []string
+	// ValidArgsFunction is an optional function that provides valid non-flag arguments for bash completion.
+	// It is a dynamic version of using ValidArgs.
+	// Only one of ValidArgs and ValidArgsFunction can be used for a command.
+	ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
 
 	// Expected arguments
 	Args PositionalArgs
@@ -83,7 +85,8 @@ type Command struct {
 
 	// Version defines the version for this command. If this value is non-empty and the command does not
 	// define a "version" flag, a "version" boolean flag will be added to the command and, if specified,
-	// will print content of the "Version" variable.
+	// will print content of the "Version" variable. A shorthand "v" flag will also be added if the
+	// command does not define one.
 	Version string
 
 	// The *Run functions are executed in the following order:
@@ -143,9 +146,11 @@ type Command struct {
 	// TraverseChildren parses flags on all parents before executing child command.
 	TraverseChildren bool
 
-	//FParseErrWhitelist flag parse errors to be ignored
+	// FParseErrWhitelist flag parse errors to be ignored
 	FParseErrWhitelist FParseErrWhitelist
 
+	ctx context.Context
+
 	// commands is the list of commands supported by this program.
 	commands []*Command
 	// parent is a parent command for this command.
@@ -205,6 +210,12 @@ type Command struct {
 	errWriter io.Writer
 }
 
+// Context returns underlying command context. If command wasn't
+// executed with ExecuteContext Context returns Background context.
+func (c *Command) Context() context.Context {
+	return c.ctx
+}
+
 // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
 // particularly useful when testing.
 func (c *Command) SetArgs(a []string) {
@@ -300,7 +311,7 @@ func (c *Command) ErrOrStderr() io.Writer {
 	return c.getErr(os.Stderr)
 }
 
-// InOrStdin returns output to stderr
+// InOrStdin returns input to stdin
 func (c *Command) InOrStdin() io.Reader {
 	return c.getIn(os.Stdin)
 }
@@ -791,7 +802,7 @@ func (c *Command) execute(a []string) (err error) {
 	}
 
 	if !c.Runnable() {
-		return ErrSubCommandRequired
+		return flag.ErrHelp
 	}
 
 	c.preRun()
@@ -862,6 +873,13 @@ func (c *Command) preRun() {
 	}
 }
 
+// ExecuteContext is the same as Execute(), but sets the ctx on the command.
+// Retrieve ctx by calling cmd.Context() inside your *Run lifecycle functions.
+func (c *Command) ExecuteContext(ctx context.Context) error {
+	c.ctx = ctx
+	return c.Execute()
+}
+
 // Execute uses the args (os.Args[1:] by default)
 // and run through the command tree finding appropriate matches
 // for commands and then corresponding flags.
@@ -872,6 +890,10 @@ func (c *Command) Execute() error {
 
 // ExecuteC executes the command.
 func (c *Command) ExecuteC() (cmd *Command, err error) {
+	if c.ctx == nil {
+		c.ctx = context.Background()
+	}
+
 	// Regardless of what command execute is called on, run on Root only
 	if c.HasParent() {
 		return c.Root().ExecuteC()
@@ -893,6 +915,9 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		args = os.Args[1:]
 	}
 
+	// initialize the hidden command to be used for bash completion
+	c.initCompleteCmd(args)
+
 	var flags []string
 	if c.TraverseChildren {
 		cmd, flags, err = c.Traverse(args)
@@ -916,6 +941,12 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		cmd.commandCalledAs.name = cmd.Name()
 	}
 
+	// We have to pass global context to children command
+	// if context is present on the parent command.
+	if cmd.ctx == nil {
+		cmd.ctx = c.ctx
+	}
+
 	err = cmd.execute(flags)
 	if err != nil {
 		// Always show help if requested, even if SilenceErrors is in
@@ -925,14 +956,6 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 			return cmd, nil
 		}
 
-		// If command wasn't runnable, show full help, but do return the error.
-		// This will result in apps by default returning a non-success exit code, but also gives them the option to
-		// handle specially.
-		if err == ErrSubCommandRequired {
-			cmd.HelpFunc()(cmd, args)
-			return cmd, err
-		}
-
 		// If root command has SilentErrors flagged,
 		// all subcommands should respect it
 		if !cmd.SilenceErrors && !c.SilenceErrors {
@@ -1007,7 +1030,11 @@ func (c *Command) InitDefaultVersionFlag() {
 		} else {
 			usage += c.Name()
 		}
-		c.Flags().Bool("version", false, usage)
+		if c.Flags().ShorthandLookup("v") == nil {
+			c.Flags().BoolP("version", "v", false, usage)
+		} else {
+			c.Flags().Bool("version", false, usage)
+		}
 	}
 }
 
@@ -1560,7 +1587,7 @@ func (c *Command) ParseFlags(args []string) error {
 	beforeErrorBufLen := c.flagErrorBuf.Len()
 	c.mergePersistentFlags()
 
-	//do it here after merging all flags and just before parse
+	// do it here after merging all flags and just before parse
 	c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist)
 
 	err := c.Flags().Parse(args)
diff --git a/custom_completions.go b/custom_completions.go
new file mode 100644
index 0000000..81f1bc3
--- /dev/null
+++ b/custom_completions.go
@@ -0,0 +1,299 @@
+package cobra
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/spf13/pflag"
+)
+
+// ShellCompRequestCmd is the name of the hidden command that is used to request
+// completion results from the program.  It is used by the shell completion script.
+const ShellCompRequestCmd = "__complete"
+
+// Global map of flag completion functions.
+var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){}
+
+// ShellCompDirective is a bit map representing the different behaviors the shell
+// can be instructed to have once completions have been provided.
+type ShellCompDirective int
+
+const (
+	// ShellCompDirectiveError indicates an error occurred and completions should be ignored.
+	ShellCompDirectiveError ShellCompDirective = 1 << iota
+
+	// ShellCompDirectiveNoSpace indicates that the shell should not add a space
+	// after the completion even if there is a single completion provided.
+	ShellCompDirectiveNoSpace
+
+	// ShellCompDirectiveNoFileComp indicates that the shell should not provide
+	// file completion even when no completion is provided.
+	// This currently does not work for zsh or bash < 4
+	ShellCompDirectiveNoFileComp
+
+	// ShellCompDirectiveDefault indicates to let the shell perform its default
+	// behavior after completions have been provided.
+	ShellCompDirectiveDefault ShellCompDirective = 0
+)
+
+// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
+func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error {
+	flag := c.Flag(flagName)
+	if flag == nil {
+		return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
+	}
+	if _, exists := flagCompletionFunctions[flag]; exists {
+		return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
+	}
+	flagCompletionFunctions[flag] = f
+	return nil
+}
+
+// Returns a string listing the different directive enabled in the specified parameter
+func (d ShellCompDirective) string() string {
+	var directives []string
+	if d&ShellCompDirectiveError != 0 {
+		directives = append(directives, "ShellCompDirectiveError")
+	}
+	if d&ShellCompDirectiveNoSpace != 0 {
+		directives = append(directives, "ShellCompDirectiveNoSpace")
+	}
+	if d&ShellCompDirectiveNoFileComp != 0 {
+		directives = append(directives, "ShellCompDirectiveNoFileComp")
+	}
+	if len(directives) == 0 {
+		directives = append(directives, "ShellCompDirectiveDefault")
+	}
+
+	if d > ShellCompDirectiveError+ShellCompDirectiveNoSpace+ShellCompDirectiveNoFileComp {
+		return fmt.Sprintf("ERROR: unexpected ShellCompDirective value: %d", d)
+	}
+	return strings.Join(directives, ", ")
+}
+
+// Adds a special hidden command that can be used to request custom completions.
+func (c *Command) initCompleteCmd(args []string) {
+	completeCmd := &Command{
+		Use:                   fmt.Sprintf("%s [command-line]", ShellCompRequestCmd),
+		DisableFlagsInUseLine: true,
+		Hidden:                true,
+		DisableFlagParsing:    true,
+		Args:                  MinimumNArgs(1),
+		Short:                 "Request shell completion choices for the specified command-line",
+		Long: fmt.Sprintf("%[2]s is a special command that is used by the shell completion logic\n%[1]s",
+			"to request completion choices for the specified command-line.", ShellCompRequestCmd),
+		Run: func(cmd *Command, args []string) {
+			finalCmd, completions, directive, err := cmd.getCompletions(args)
+			if err != nil {
+				CompErrorln(err.Error())
+				// Keep going for multiple reasons:
+				// 1- There could be some valid completions even though there was an error
+				// 2- Even without completions, we need to print the directive
+			}
+
+			for _, comp := range completions {
+				// Print each possible completion to stdout for the completion script to consume.
+				fmt.Fprintln(finalCmd.OutOrStdout(), comp)
+			}
+
+			if directive > ShellCompDirectiveError+ShellCompDirectiveNoSpace+ShellCompDirectiveNoFileComp {
+				directive = ShellCompDirectiveDefault
+			}
+
+			// As the last printout, print the completion directive for the completion script to parse.
+			// The directive integer must be that last character following a single colon (:).
+			// The completion script expects :<directive>
+			fmt.Fprintf(finalCmd.OutOrStdout(), ":%d\n", directive)
+
+			// Print some helpful info to stderr for the user to understand.
+			// Output from stderr must be ignored by the completion script.
+			fmt.Fprintf(finalCmd.ErrOrStderr(), "Completion ended with directive: %s\n", directive.string())
+		},
+	}
+	c.AddCommand(completeCmd)
+	subCmd, _, err := c.Find(args)
+	if err != nil || subCmd.Name() != ShellCompRequestCmd {
+		// Only create this special command if it is actually being called.
+		// This reduces possible side-effects of creating such a command;
+		// for example, having this command would cause problems to a
+		// cobra program that only consists of the root command, since this
+		// command would cause the root command to suddenly have a subcommand.
+		c.RemoveCommand(completeCmd)
+	}
+}
+
+func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) {
+	var completions []string
+
+	// The last argument, which is not completely typed by the user,
+	// should not be part of the list of arguments
+	toComplete := args[len(args)-1]
+	trimmedArgs := args[:len(args)-1]
+
+	// Find the real command for which completion must be performed
+	finalCmd, finalArgs, err := c.Root().Find(trimmedArgs)
+	if err != nil {
+		// Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
+		return c, completions, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs)
+	}
+
+	var flag *pflag.Flag
+	if !finalCmd.DisableFlagParsing {
+		// We only do flag completion if we are allowed to parse flags
+		// This is important for commands which have requested to do their own flag completion.
+		flag, finalArgs, toComplete, err = checkIfFlagCompletion(finalCmd, finalArgs, toComplete)
+		if err != nil {
+			// Error while attempting to parse flags
+			return finalCmd, completions, ShellCompDirectiveDefault, err
+		}
+	}
+
+	// Parse the flags and extract the arguments to prepare for calling the completion function
+	if err = finalCmd.ParseFlags(finalArgs); err != nil {
+		return finalCmd, completions, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error())
+	}
+
+	// We only remove the flags from the arguments if DisableFlagParsing is not set.
+	// This is important for commands which have requested to do their own flag completion.
+	if !finalCmd.DisableFlagParsing {
+		finalArgs = finalCmd.Flags().Args()
+	}
+
+	// Find the completion function for the flag or command
+	var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
+	if flag != nil {
+		completionFn = flagCompletionFunctions[flag]
+	} else {
+		completionFn = finalCmd.ValidArgsFunction
+	}
+	if completionFn == nil {
+		// Go custom completion not supported/needed for this flag or command
+		return finalCmd, completions, ShellCompDirectiveDefault, nil
+	}
+
+	// Call the registered completion function to get the completions
+	comps, directive := completionFn(finalCmd, finalArgs, toComplete)
+	completions = append(completions, comps...)
+	return finalCmd, completions, directive, nil
+}
+
+func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) {
+	var flagName string
+	trimmedArgs := args
+	flagWithEqual := false
+	if isFlagArg(lastArg) {
+		if index := strings.Index(lastArg, "="); index >= 0 {
+			flagName = strings.TrimLeft(lastArg[:index], "-")
+			lastArg = lastArg[index+1:]
+			flagWithEqual = true
+		} else {
+			return nil, nil, "", errors.New("Unexpected completion request for flag")
+		}
+	}
+
+	if len(flagName) == 0 {
+		if len(args) > 0 {
+			prevArg := args[len(args)-1]
+			if isFlagArg(prevArg) {
+				// Only consider the case where the flag does not contain an =.
+				// If the flag contains an = it means it has already been fully processed,
+				// so we don't need to deal with it here.
+				if index := strings.Index(prevArg, "="); index < 0 {
+					flagName = strings.TrimLeft(prevArg, "-")
+
+					// Remove the uncompleted flag or else there could be an error created
+					// for an invalid value for that flag
+					trimmedArgs = args[:len(args)-1]
+				}
+			}
+		}
+	}
+
+	if len(flagName) == 0 {
+		// Not doing flag completion
+		return nil, trimmedArgs, lastArg, nil
+	}
+
+	flag := findFlag(finalCmd, flagName)
+	if flag == nil {
+		// Flag not supported by this command, nothing to complete
+		err := fmt.Errorf("Subcommand '%s' does not support flag '%s'", finalCmd.Name(), flagName)
+		return nil, nil, "", err
+	}
+
+	if !flagWithEqual {
+		if len(flag.NoOptDefVal) != 0 {
+			// We had assumed dealing with a two-word flag but the flag is a boolean flag.
+			// In that case, there is no value following it, so we are not really doing flag completion.
+			// Reset everything to do noun completion.
+			trimmedArgs = args
+			flag = nil
+		}
+	}
+
+	return flag, trimmedArgs, lastArg, nil
+}
+
+func findFlag(cmd *Command, name string) *pflag.Flag {
+	flagSet := cmd.Flags()
+	if len(name) == 1 {
+		// First convert the short flag into a long flag
+		// as the cmd.Flag() search only accepts long flags
+		if short := flagSet.ShorthandLookup(name); short != nil {
+			name = short.Name
+		} else {
+			set := cmd.InheritedFlags()
+			if short = set.ShorthandLookup(name); short != nil {
+				name = short.Name
+			} else {
+				return nil
+			}
+		}
+	}
+	return cmd.Flag(name)
+}
+
+// CompDebug prints the specified string to the same file as where the
+// completion script prints its logs.
+// Note that completion printouts should never be on stdout as they would
+// be wrongly interpreted as actual completion choices by the completion script.
+func CompDebug(msg string, printToStdErr bool) {
+	msg = fmt.Sprintf("[Debug] %s", msg)
+
+	// Such logs are only printed when the user has set the environment
+	// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
+	if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" {
+		f, err := os.OpenFile(path,
+			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+		if err == nil {
+			defer f.Close()
+			f.WriteString(msg)
+		}
+	}
+
+	if printToStdErr {
+		// Must print to stderr for this not to be read by the completion script.
+		fmt.Fprintf(os.Stderr, msg)
+	}
+}
+
+// CompDebugln prints the specified string with a newline at the end
+// to the same file as where the completion script prints its logs.
+// Such logs are only printed when the user has set the environment
+// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
+func CompDebugln(msg string, printToStdErr bool) {
+	CompDebug(fmt.Sprintf("%s\n", msg), printToStdErr)
+}
+
+// CompError prints the specified completion message to stderr.
+func CompError(msg string) {
+	msg = fmt.Sprintf("[Error] %s", msg)
+	CompDebug(msg, true)
+}
+
+// CompErrorln prints the specified completion message to stderr with a newline at the end.
+func CompErrorln(msg string) {
+	CompError(fmt.Sprintf("%s\n", msg))
+}


Test patch:
diff --git a/cobra/cmd/golden_test.go b/cobra/cmd/golden_test.go
index 9010caa..e4055f3 100644
--- a/cobra/cmd/golden_test.go
+++ b/cobra/cmd/golden_test.go
@@ -13,8 +13,10 @@ var update = flag.Bool("update", false, "update .golden files")
 
 func init() {
 	// Mute commands.
-	addCmd.SetOutput(new(bytes.Buffer))
-	initCmd.SetOutput(new(bytes.Buffer))
+	addCmd.SetOut(new(bytes.Buffer))
+	addCmd.SetErr(new(bytes.Buffer))
+	initCmd.SetOut(new(bytes.Buffer))
+	initCmd.SetErr(new(bytes.Buffer))
 }
 
 // ensureLF converts any \r\n to \n
diff --git a/command_test.go b/command_test.go
index b26bd4a..8ec52f4 100644
--- a/command_test.go
+++ b/command_test.go
@@ -2,6 +2,7 @@ package cobra
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"os"
 	"reflect"
@@ -18,9 +19,21 @@ func executeCommand(root *Command, args ...string) (output string, err error) {
 	return output, err
 }
 
+func executeCommandWithContext(ctx context.Context, root *Command, args ...string) (output string, err error) {
+	buf := new(bytes.Buffer)
+	root.SetOut(buf)
+	root.SetErr(buf)
+	root.SetArgs(args)
+
+	err = root.ExecuteContext(ctx)
+
+	return buf.String(), err
+}
+
 func executeCommandC(root *Command, args ...string) (c *Command, output string, err error) {
 	buf := new(bytes.Buffer)
-	root.SetOutput(buf)
+	root.SetOut(buf)
+	root.SetErr(buf)
 	root.SetArgs(args)
 
 	c, err = root.ExecuteC()
@@ -135,6 +148,62 @@ func TestSubcommandExecuteC(t *testing.T) {
 	}
 }
 
+func TestExecuteContext(t *testing.T) {
+	ctx := context.TODO()
+
+	ctxRun := func(cmd *Command, args []string) {
+		if cmd.Context() != ctx {
+			t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
+		}
+	}
+
+	rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
+	childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
+	granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
+
+	childCmd.AddCommand(granchildCmd)
+	rootCmd.AddCommand(childCmd)
+
+	if _, err := executeCommandWithContext(ctx, rootCmd, ""); err != nil {
+		t.Errorf("Root command must not fail: %+v", err)
+	}
+
+	if _, err := executeCommandWithContext(ctx, rootCmd, "child"); err != nil {
+		t.Errorf("Subcommand must not fail: %+v", err)
+	}
+
+	if _, err := executeCommandWithContext(ctx, rootCmd, "child", "grandchild"); err != nil {
+		t.Errorf("Command child must not fail: %+v", err)
+	}
+}
+
+func TestExecute_NoContext(t *testing.T) {
+	run := func(cmd *Command, args []string) {
+		if cmd.Context() != context.Background() {
+			t.Errorf("Command %s must have background context", cmd.Use)
+		}
+	}
+
+	rootCmd := &Command{Use: "root", Run: run, PreRun: run}
+	childCmd := &Command{Use: "child", Run: run, PreRun: run}
+	granchildCmd := &Command{Use: "grandchild", Run: run, PreRun: run}
+
+	childCmd.AddCommand(granchildCmd)
+	rootCmd.AddCommand(childCmd)
+
+	if _, err := executeCommand(rootCmd, ""); err != nil {
+		t.Errorf("Root command must not fail: %+v", err)
+	}
+
+	if _, err := executeCommand(rootCmd, "child"); err != nil {
+		t.Errorf("Subcommand must not fail: %+v", err)
+	}
+
+	if _, err := executeCommand(rootCmd, "child", "grandchild"); err != nil {
+		t.Errorf("Command child must not fail: %+v", err)
+	}
+}
+
 func TestRootUnknownCommandSilenced(t *testing.T) {
 	rootCmd := &Command{Use: "root", Run: emptyRun}
 	rootCmd.SilenceErrors = true
@@ -836,8 +905,8 @@ func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
 	rootCmd.AddCommand(childCmd)
 
 	output, err := executeCommand(rootCmd, "child")
-	if err != ErrSubCommandRequired {
-		t.Errorf("Expected error")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
 	}
 
 	checkStringContains(t, output, childCmd.Long)
@@ -854,6 +923,52 @@ func TestVersionFlagExecuted(t *testing.T) {
 	checkStringContains(t, output, "root version 1.0.0")
 }
 
+func TestVersionFlagExecutedWithNoName(t *testing.T) {
+	rootCmd := &Command{Version: "1.0.0", Run: emptyRun}
+
+	output, err := executeCommand(rootCmd, "--version", "arg1")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	checkStringContains(t, output, "version 1.0.0")
+}
+
+func TestShortAndLongVersionFlagInHelp(t *testing.T) {
+	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
+
+	output, err := executeCommand(rootCmd, "--help")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	checkStringContains(t, output, "-v, --version")
+}
+
+func TestLongVersionFlagOnlyInHelpWhenShortPredefined(t *testing.T) {
+	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
+	rootCmd.Flags().StringP("foo", "v", "", "not a version flag")
+
+	output, err := executeCommand(rootCmd, "--help")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	checkStringOmits(t, output, "-v, --version")
+	checkStringContains(t, output, "--version")
+}
+
+func TestShorthandVersionFlagExecuted(t *testing.T) {
+	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
+
+	output, err := executeCommand(rootCmd, "-v", "arg1")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	checkStringContains(t, output, "root version 1.0.0")
+}
+
 func TestVersionTemplate(t *testing.T) {
 	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
 	rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
@@ -866,6 +981,18 @@ func TestVersionTemplate(t *testing.T) {
 	checkStringContains(t, output, "customized version: 1.0.0")
 }
 
+func TestShorthandVersionTemplate(t *testing.T) {
+	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
+	rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
+
+	output, err := executeCommand(rootCmd, "-v", "arg1")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	checkStringContains(t, output, "customized version: 1.0.0")
+}
+
 func TestVersionFlagExecutedOnSubcommand(t *testing.T) {
 	rootCmd := &Command{Use: "root", Version: "1.0.0"}
 	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
@@ -878,6 +1005,18 @@ func TestVersionFlagExecutedOnSubcommand(t *testing.T) {
 	checkStringContains(t, output, "root version 1.0.0")
 }
 
+func TestShorthandVersionFlagExecutedOnSubcommand(t *testing.T) {
+	rootCmd := &Command{Use: "root", Version: "1.0.0"}
+	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
+
+	output, err := executeCommand(rootCmd, "-v", "sub")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	checkStringContains(t, output, "root version 1.0.0")
+}
+
 func TestVersionFlagOnlyAddedToRoot(t *testing.T) {
 	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
 	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
@@ -890,6 +1029,18 @@ func TestVersionFlagOnlyAddedToRoot(t *testing.T) {
 	checkStringContains(t, err.Error(), "unknown flag: --version")
 }
 
+func TestShortVersionFlagOnlyAddedToRoot(t *testing.T) {
+	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
+	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
+
+	_, err := executeCommand(rootCmd, "sub", "-v")
+	if err == nil {
+		t.Errorf("Expected error")
+	}
+
+	checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
+}
+
 func TestVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
 	rootCmd := &Command{Use: "root", Run: emptyRun}
 
@@ -900,6 +1051,39 @@ func TestVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
 	checkStringContains(t, err.Error(), "unknown flag: --version")
 }
 
+func TestShorthandVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
+	rootCmd := &Command{Use: "root", Run: emptyRun}
+
+	_, err := executeCommand(rootCmd, "-v")
+	if err == nil {
+		t.Errorf("Expected error")
+	}
+	checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
+}
+
+func TestShorthandVersionFlagOnlyAddedIfShorthandNotDefined(t *testing.T) {
+	rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
+	rootCmd.Flags().StringP("notversion", "v", "", "not a version flag")
+
+	_, err := executeCommand(rootCmd, "-v")
+	if err == nil {
+		t.Errorf("Expected error")
+	}
+	check(t, rootCmd.Flags().ShorthandLookup("v").Name, "notversion")
+	checkStringContains(t, err.Error(), "flag needs an argument: 'v' in -v")
+}
+
+func TestShorthandVersionFlagOnlyAddedIfVersionNotDefined(t *testing.T) {
+	rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
+	rootCmd.Flags().Bool("version", false, "a different kind of version flag")
+
+	_, err := executeCommand(rootCmd, "-v")
+	if err == nil {
+		t.Errorf("Expected error")
+	}
+	checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
+}
+
 func TestUsageIsNotPrintedTwice(t *testing.T) {
 	var cmd = &Command{Use: "root"}
 	var sub = &Command{Use: "sub"}
diff --git a/custom_completions_test.go b/custom_completions_test.go
new file mode 100644
index 0000000..ea6a6d9
--- /dev/null
+++ b/custom_completions_test.go
@@ -0,0 +1,385 @@
+package cobra
+
+import (
+	"bytes"
+	"strings"
+	"testing"
+)
+
+func validArgsFunc(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	if len(args) != 0 {
+		return nil, ShellCompDirectiveNoFileComp
+	}
+
+	var completions []string
+	for _, comp := range []string{"one", "two"} {
+		if strings.HasPrefix(comp, toComplete) {
+			completions = append(completions, comp)
+		}
+	}
+	return completions, ShellCompDirectiveDefault
+}
+
+func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	if len(args) != 0 {
+		return nil, ShellCompDirectiveNoFileComp
+	}
+
+	var completions []string
+	for _, comp := range []string{"three", "four"} {
+		if strings.HasPrefix(comp, toComplete) {
+			completions = append(completions, comp)
+		}
+	}
+	return completions, ShellCompDirectiveDefault
+}
+
+func TestValidArgsFuncSingleCmd(t *testing.T) {
+	rootCmd := &Command{
+		Use:               "root",
+		ValidArgsFunction: validArgsFunc,
+		Run:               emptyRun,
+	}
+
+	// Test completing an empty string
+	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		"one",
+		"two",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Check completing with a prefix
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"two",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}
+
+func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) {
+	rootCmd := &Command{
+		Use: "root",
+		// If we don't specify a value for Args, this test fails.
+		// This is only true for a root command without any subcommands, and is caused
+		// by the fact that the __complete command becomes a subcommand when there should not be one.
+		// The problem is in the implementation of legacyArgs().
+		Args:              MinimumNArgs(1),
+		ValidArgsFunction: validArgsFunc,
+		Run:               emptyRun,
+	}
+
+	// Check completing with wrong number of args
+	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "unexpectedArg", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}
+
+func TestValidArgsFuncChildCmds(t *testing.T) {
+	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+	child1Cmd := &Command{
+		Use:               "child1",
+		ValidArgsFunction: validArgsFunc,
+		Run:               emptyRun,
+	}
+	child2Cmd := &Command{
+		Use:               "child2",
+		ValidArgsFunction: validArgsFunc2,
+		Run:               emptyRun,
+	}
+	rootCmd.AddCommand(child1Cmd, child2Cmd)
+
+	// Test completion of first sub-command with empty argument
+	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		"one",
+		"two",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test completion of first sub-command with a prefix to complete
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"two",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Check completing with wrong number of args
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test completion of second sub-command with empty argument
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"three",
+		"four",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"three",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Check completing with wrong number of args
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}
+
+func TestValidArgsFuncAliases(t *testing.T) {
+	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+	child := &Command{
+		Use:               "child",
+		Aliases:           []string{"son", "daughter"},
+		ValidArgsFunction: validArgsFunc,
+		Run:               emptyRun,
+	}
+	rootCmd.AddCommand(child)
+
+	// Test completion of first sub-command with empty argument
+	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "son", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		"one",
+		"two",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test completion of first sub-command with a prefix to complete
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "daughter", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"two",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Check completing with wrong number of args
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "son", "unexpectedArg", "t")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}
+
+func TestValidArgsFuncInScript(t *testing.T) {
+	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+	child := &Command{
+		Use:               "child",
+		ValidArgsFunction: validArgsFunc,
+		Run:               emptyRun,
+	}
+	rootCmd.AddCommand(child)
+
+	buf := new(bytes.Buffer)
+	rootCmd.GenBashCompletion(buf)
+	output := buf.String()
+
+	check(t, output, "has_completion_function=1")
+}
+
+func TestNoValidArgsFuncInScript(t *testing.T) {
+	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+	child := &Command{
+		Use: "child",
+		Run: emptyRun,
+	}
+	rootCmd.AddCommand(child)
+
+	buf := new(bytes.Buffer)
+	rootCmd.GenBashCompletion(buf)
+	output := buf.String()
+
+	checkOmit(t, output, "has_completion_function=1")
+}
+
+func TestFlagCompletionInGo(t *testing.T) {
+	rootCmd := &Command{
+		Use: "root",
+		Run: emptyRun,
+	}
+	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
+	rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+		completions := []string{}
+		for _, comp := range []string{"1", "2", "10"} {
+			if strings.HasPrefix(comp, toComplete) {
+				completions = append(completions, comp)
+			}
+		}
+		return completions, ShellCompDirectiveDefault
+	})
+	rootCmd.Flags().String("filename", "", "Enter a filename")
+	rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+		completions := []string{}
+		for _, comp := range []string{"file.yaml", "myfile.json", "file.xml"} {
+			if strings.HasPrefix(comp, toComplete) {
+				completions = append(completions, comp)
+			}
+		}
+		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
+	})
+
+	// Test completing an empty string
+	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		"1",
+		"2",
+		"10",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Check completing with a prefix
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"1",
+		"10",
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test completing an empty string
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"file.yaml",
+		"myfile.json",
+		"file.xml",
+		":6",
+		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Check completing with a prefix
+	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"file.yaml",
+		"file.xml",
+		":6",
+		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}