From 84a1965f142cf6e48f2dd3aeb3b2aeaafd393551 Mon Sep 17 00:00:00 2001
From: Harald Albers <github@albersweb.de>
Date: Tue, 4 Feb 2025 20:41:38 +0000
Subject: [PATCH 1/2] The default ShellCompDirective can be customized.

---
 completions.go                     | 15 ++++++++
 completions_test.go                | 57 ++++++++++++++++++++++++++++++
 site/content/completions/_index.md | 28 +++++++++++++++
 3 files changed, 100 insertions(+)

diff --git a/completions.go b/completions.go
index a1752f76..0733c5bb 100644
--- a/completions.go
+++ b/completions.go
@@ -115,6 +115,13 @@ type CompletionOptions struct {
 	DisableDescriptions bool
 	// HiddenDefaultCmd makes the default 'completion' command hidden
 	HiddenDefaultCmd bool
+	// DefaultShellCompDirective sets the ShellCompDirective that is returned
+	// if no special directive can be determined
+	DefaultShellCompDirective *ShellCompDirective
+}
+
+func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) {
+	receiver.DefaultShellCompDirective = &directive
 }
 
 // Completion is a string that can be used for completions
@@ -480,6 +487,14 @@ func (c *Command) getCompletions(args []string) (*Command, []Completion, ShellCo
 		}
 	} else {
 		directive = ShellCompDirectiveDefault
+		// check current and parent commands for a custom DefaultShellCompDirective
+		for cmd := finalCmd; cmd != nil; cmd = cmd.parent {
+			if cmd.CompletionOptions.DefaultShellCompDirective != nil {
+				directive = *cmd.CompletionOptions.DefaultShellCompDirective
+				break
+			}
+		}
+
 		if flag == nil {
 			foundLocalNonPersistentFlag := false
 			// If TraverseChildren is true on the root command we don't check for
diff --git a/completions_test.go b/completions_test.go
index 89da3d50..894a09f1 100644
--- a/completions_test.go
+++ b/completions_test.go
@@ -4016,3 +4016,60 @@ func TestInitDefaultCompletionCmd(t *testing.T) {
 		})
 	}
 }
+
+func TestCustomDefaultShellCompDirective(t *testing.T) {
+	rootCmd := &Command{Use: "root", Run: emptyRun}
+	rootCmd.PersistentFlags().String("string", "", "test string flag")
+	// use ShellCompDirectiveNoFileComp instead of the default, which is ShellCompDirectiveDefault.
+	rootCmd.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveNoFileComp)
+
+	// child1 inherits the custom DefaultShellCompDirective.
+	childCmd1 := &Command{Use: "child1", Run: emptyRun}
+	// child2 resets the custom DefaultShellCompDirective to the default value.
+	childCmd2 := &Command{Use: "child2", Run: emptyRun}
+	childCmd2.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveDefault)
+
+	rootCmd.AddCommand(childCmd1, childCmd2)
+
+	testCases := []struct {
+		desc              string
+		args              []string
+		expectedDirective string
+	}{
+		{
+			"flag completion on root command with custom DefaultShellCompDirective",
+			[]string{"--string", ""},
+			"ShellCompDirectiveNoFileComp",
+		},
+		{
+			"flag completion on subcommand with inherited custom DefaultShellCompDirective",
+			[]string{"child1", "--string", ""},
+			"ShellCompDirectiveNoFileComp",
+		},
+		{
+			"flag completion on subcommand with reset DefaultShellCompDirective",
+			[]string{"child2", "--string", ""},
+			"ShellCompDirectiveDefault",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			args := []string{ShellCompNoDescRequestCmd}
+			args = append(args, tc.args...)
+
+			output, err := executeCommand(rootCmd, args...)
+
+			if err != nil {
+				t.Errorf("Unexpected error: %v", err)
+			}
+
+			outputWords := strings.Split(strings.TrimSpace(output), " ")
+			directive := outputWords[len(outputWords)-1]
+
+			if directive != tc.expectedDirective {
+				t.Errorf("expected: %q, got: %q", tc.expectedDirective, directive)
+			}
+		})
+	}
+}
diff --git a/site/content/completions/_index.md b/site/content/completions/_index.md
index 8445850a..2595a178 100644
--- a/site/content/completions/_index.md
+++ b/site/content/completions/_index.md
@@ -305,6 +305,34 @@ $ helm status --output [tab][tab]
 json table yaml
 ```
 
+#### Change the default ShellCompDirective
+
+When no completion function is registered for a leaf command or for a flag, Cobra will
+automatically use `ShellCompDirectiveDefault`, which will invoke the shell's filename completion.
+This implies that when file completion does not apply to a leaf command or to a flag (the command
+or flag does not operate on a filename), turning off file completion requires you to register a
+completion function for that command/flag.
+For example:
+
+```go
+cmd.RegisterFlagCompletionFunc("flag-name", cobra.NoFileCompletions)
+```
+
+If you find that there are more situations where file completion should be turned off than
+when it is applicable, you can change the default `ShellCompDirective` for a command
+and its subcommands to `ShellCompDirectiveNoFileComp`:
+
+```go
+cmd.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveNoFileComp)
+```
+
+If doing so, keep in mind that you should instead register a completion function for leaf commands or
+flags where file completion is applicable. For example:
+
+```go
+cmd.RegisterFlagCompletionFunc("flag-name", cobra.FixedCompletions(nil, ShellCompDirectiveDefault))
+```
+
 #### Debugging
 
 You can also easily debug your Go completion code for flags:

From 6ad4cd6904cb4ea79cced0ae5a6298721258a73d Mon Sep 17 00:00:00 2001
From: Harald Albers <github@albersweb.de>
Date: Thu, 6 Mar 2025 12:26:08 +0000
Subject: [PATCH 2/2] Improve documentation of DefaultShellCompDirective

---
 site/content/completions/_index.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/site/content/completions/_index.md b/site/content/completions/_index.md
index 2595a178..0115910f 100644
--- a/site/content/completions/_index.md
+++ b/site/content/completions/_index.md
@@ -319,7 +319,7 @@ cmd.RegisterFlagCompletionFunc("flag-name", cobra.NoFileCompletions)
 ```
 
 If you find that there are more situations where file completion should be turned off than
-when it is applicable, you can change the default `ShellCompDirective` for a command
+when it is applicable, you can recursively change the default `ShellCompDirective` for a command
 and its subcommands to `ShellCompDirectiveNoFileComp`:
 
 ```go
@@ -333,6 +333,8 @@ flags where file completion is applicable. For example:
 cmd.RegisterFlagCompletionFunc("flag-name", cobra.FixedCompletions(nil, ShellCompDirectiveDefault))
 ```
 
+To change the default directive for the entire program, set the DefaultShellCompDirective on the root command.
+
 #### Debugging
 
 You can also easily debug your Go completion code for flags: