From e4d96b75c149feac932fcba8869a70d028924ae7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= <zan10d@gmail.com>
Date: Fri, 17 Jan 2025 22:27:51 +0100
Subject: [PATCH 1/6] Make command suggestion messages configurable.

---
 args.go         |  4 ++--
 command.go      | 18 ++++++++++++++++++
 command_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/args.go b/args.go
index ed1e70ce..92e1dde0 100644
--- a/args.go
+++ b/args.go
@@ -33,7 +33,7 @@ func legacyArgs(cmd *Command, args []string) error {
 
 	// root command with subcommands, do subcommand checking.
 	if !cmd.HasParent() && len(args) > 0 {
-		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
+		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.SuggestFunc()(args[0]))
 	}
 	return nil
 }
@@ -58,7 +58,7 @@ func OnlyValidArgs(cmd *Command, args []string) error {
 		}
 		for _, v := range args {
 			if !stringInSlice(v, validArgs) {
-				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
+				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.SuggestFunc()(args[0]))
 			}
 		}
 	}
diff --git a/command.go b/command.go
index 19602946..abf3f06c 100644
--- a/command.go
+++ b/command.go
@@ -180,6 +180,8 @@ type Command struct {
 	helpCommand *Command
 	// helpCommandGroupID is the group id for the helpCommand
 	helpCommandGroupID string
+	// suggestFunc is suggest func defined by the user.
+	suggestFunc func(string) string
 
 	// completionCommandGroupID is the group id for the completion command
 	completionCommandGroupID string
@@ -340,6 +342,10 @@ func (c *Command) SetHelpCommandGroupID(groupID string) {
 	c.helpCommandGroupID = groupID
 }
 
+func (c *Command) SetSuggestFunc(f func(string) string) {
+	c.suggestFunc = f
+}
+
 // SetCompletionCommandGroupID sets the group id of the completion command.
 func (c *Command) SetCompletionCommandGroupID(groupID string) {
 	// completionCommandGroupID is used if no completion command is defined by the user
@@ -477,6 +483,18 @@ func (c *Command) Help() error {
 	return nil
 }
 
+// SuggestFunc returns either the function set by SetSuggestFunc for this command
+// or a parent, or it returns a function with default suggestion behavior.
+func (c *Command) SuggestFunc() func(string) string {
+	if c.suggestFunc != nil && !c.DisableSuggestions {
+		return c.suggestFunc
+	}
+	if c.HasParent() {
+		return c.Parent().SuggestFunc()
+	}
+	return c.findSuggestions
+}
+
 // UsageString returns usage string.
 func (c *Command) UsageString() string {
 	// Storing normal writers
diff --git a/command_test.go b/command_test.go
index 837b6b30..16df3a53 100644
--- a/command_test.go
+++ b/command_test.go
@@ -1393,6 +1393,48 @@ func TestSuggestions(t *testing.T) {
 	}
 }
 
+func TestCustomSuggestions(t *testing.T) {
+	templateWithCustomSuggestions := "Error: unknown command \"%s\" for \"root\"\nSome custom suggestion.\n\nRun 'root --help' for usage.\n"
+	templateWithDefaultSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
+	templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
+
+	for typo, suggestion := range map[string]string{"time": "times", "timse": "times"} {
+		for _, suggestionsDisabled := range []bool{true, false} {
+			for _, setCustomSuggest := range []bool{true, false} {
+				rootCmd := &Command{Use: "root", Run: emptyRun}
+				timesCmd := &Command{
+					Use: "times",
+					Run: emptyRun,
+				}
+				rootCmd.AddCommand(timesCmd)
+
+				rootCmd.DisableSuggestions = suggestionsDisabled
+
+				if setCustomSuggest {
+					rootCmd.SetSuggestFunc(func(a string) string {
+						return "\nSome custom suggestion.\n"
+					})
+				}
+
+				var expected string
+				if suggestionsDisabled {
+					expected = fmt.Sprintf(templateWithoutSuggestions, typo)
+				} else if setCustomSuggest {
+					expected = fmt.Sprintf(templateWithCustomSuggestions, typo)
+				} else {
+					expected = fmt.Sprintf(templateWithDefaultSuggestions, typo, suggestion)
+				}
+
+				output, _ := executeCommand(rootCmd, typo)
+
+				if output != expected {
+					t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
+				}
+			}
+		}
+	}
+}
+
 func TestCaseInsensitive(t *testing.T) {
 	rootCmd := &Command{Use: "root", Run: emptyRun}
 	childCmd := &Command{Use: "child", Run: emptyRun, Aliases: []string{"alternative"}}

From 84b14d4e50375e2f3fd3f03033d8b6e0d851e60e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= <zan10d@gmail.com>
Date: Sat, 18 Jan 2025 00:14:46 +0100
Subject: [PATCH 2/6] Simplify calling of the suggestion function.

---
 args.go    |  4 ++--
 command.go | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/args.go b/args.go
index 92e1dde0..c86b1bb4 100644
--- a/args.go
+++ b/args.go
@@ -33,7 +33,7 @@ func legacyArgs(cmd *Command, args []string) error {
 
 	// root command with subcommands, do subcommand checking.
 	if !cmd.HasParent() && len(args) > 0 {
-		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.SuggestFunc()(args[0]))
+		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.SuggestFunc(args[0]))
 	}
 	return nil
 }
@@ -58,7 +58,7 @@ func OnlyValidArgs(cmd *Command, args []string) error {
 		}
 		for _, v := range args {
 			if !stringInSlice(v, validArgs) {
-				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.SuggestFunc()(args[0]))
+				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.SuggestFunc(args[0]))
 			}
 		}
 	}
diff --git a/command.go b/command.go
index abf3f06c..1b3a687b 100644
--- a/command.go
+++ b/command.go
@@ -483,16 +483,16 @@ func (c *Command) Help() error {
 	return nil
 }
 
-// SuggestFunc returns either the function set by SetSuggestFunc for this command
-// or a parent, or it returns a function with default suggestion behavior.
-func (c *Command) SuggestFunc() func(string) string {
+// SuggestFunc returns suggestions for the provided typedName using either
+// the function set by SetSuggestFunc for this command, parent's or a default one.
+func (c *Command) SuggestFunc(typedName string) string {
 	if c.suggestFunc != nil && !c.DisableSuggestions {
-		return c.suggestFunc
+		return c.suggestFunc(typedName)
 	}
 	if c.HasParent() {
-		return c.Parent().SuggestFunc()
+		return c.Parent().SuggestFunc(typedName)
 	}
-	return c.findSuggestions
+	return c.findSuggestions(typedName)
 }
 
 // UsageString returns usage string.

From 90335672a922af345d72bf493e1530d761ed0cc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= <zan10d@gmail.com>
Date: Sun, 19 Jan 2025 00:36:37 +0100
Subject: [PATCH 3/6] Fix search for parent's suggest function and improve
 tests.

---
 command.go      |  23 +++++++-
 command_test.go | 142 +++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 131 insertions(+), 34 deletions(-)

diff --git a/command.go b/command.go
index 1b3a687b..2b152058 100644
--- a/command.go
+++ b/command.go
@@ -485,12 +485,31 @@ func (c *Command) Help() error {
 
 // SuggestFunc returns suggestions for the provided typedName using either
 // the function set by SetSuggestFunc for this command, parent's or a default one.
+// When searching for a parent's function, it recursively checks towards the root
+// and returns the first one found. If none found, uses direct parent's default.
 func (c *Command) SuggestFunc(typedName string) string {
-	if c.suggestFunc != nil && !c.DisableSuggestions {
+	if c.DisableSuggestions {
+		return ""
+	}
+	if c.suggestFunc != nil {
 		return c.suggestFunc(typedName)
 	}
 	if c.HasParent() {
-		return c.Parent().SuggestFunc(typedName)
+		var getParentFunc func(*Command) func(string) string
+		getParentFunc = func(parent *Command) func(string) string {
+			if parent.suggestFunc != nil {
+				return parent.suggestFunc
+			}
+			if parent.HasParent() {
+				return getParentFunc(parent.Parent())
+			}
+			return nil
+		}
+		parentFunc := getParentFunc(c.Parent())
+		if parentFunc != nil {
+			return parentFunc(typedName)
+		}
+		return c.Parent().findSuggestions(typedName)
 	}
 	return c.findSuggestions(typedName)
 }
diff --git a/command_test.go b/command_test.go
index 16df3a53..e5fad89b 100644
--- a/command_test.go
+++ b/command_test.go
@@ -1394,44 +1394,122 @@ func TestSuggestions(t *testing.T) {
 }
 
 func TestCustomSuggestions(t *testing.T) {
-	templateWithCustomSuggestions := "Error: unknown command \"%s\" for \"root\"\nSome custom suggestion.\n\nRun 'root --help' for usage.\n"
-	templateWithDefaultSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
-	templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
+	rootCmd := &Command{Use: "root", Run: emptyRun}
+	timesCmd := &Command{Use: "times", Run: emptyRun}
+	rootCmd.AddCommand(timesCmd)
 
-	for typo, suggestion := range map[string]string{"time": "times", "timse": "times"} {
-		for _, suggestionsDisabled := range []bool{true, false} {
-			for _, setCustomSuggest := range []bool{true, false} {
-				rootCmd := &Command{Use: "root", Run: emptyRun}
-				timesCmd := &Command{
-					Use: "times",
-					Run: emptyRun,
-				}
-				rootCmd.AddCommand(timesCmd)
+	var expected, output string
 
-				rootCmd.DisableSuggestions = suggestionsDisabled
+	expected = ""
+	output, _ = executeCommand(rootCmd, "times")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
 
-				if setCustomSuggest {
-					rootCmd.SetSuggestFunc(func(a string) string {
-						return "\nSome custom suggestion.\n"
-					})
-				}
+	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n", "time", "times")
+	output, _ = executeCommand(rootCmd, "time")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
 
-				var expected string
-				if suggestionsDisabled {
-					expected = fmt.Sprintf(templateWithoutSuggestions, typo)
-				} else if setCustomSuggest {
-					expected = fmt.Sprintf(templateWithCustomSuggestions, typo)
-				} else {
-					expected = fmt.Sprintf(templateWithDefaultSuggestions, typo, suggestion)
-				}
+	rootCmd.DisableSuggestions = true
 
-				output, _ := executeCommand(rootCmd, typo)
+	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n", "time")
+	output, _ = executeCommand(rootCmd, "time")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
 
-				if output != expected {
-					t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
-				}
-			}
-		}
+	rootCmd.DisableSuggestions = false
+	rootCmd.SetSuggestFunc(func(typedName string) string {
+		return "\nSome custom suggestion.\n"
+	})
+
+	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\nSome custom suggestion.\n\nRun 'root --help' for usage.\n", "time")
+	output, _ = executeCommand(rootCmd, "time")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+}
+
+func TestCustomSuggestions_OnlyValidArgs(t *testing.T) {
+	validArgs := []string{"a"}
+	rootCmd := &Command{Use: "root", Args: OnlyValidArgs, Run: emptyRun, ValidArgs: validArgs}
+	grandparentCmd := &Command{Use: "grandparent", Args: OnlyValidArgs, Run: emptyRun, ValidArgs: validArgs}
+	parentCmd := &Command{Use: "parent", Args: OnlyValidArgs, Run: emptyRun, ValidArgs: validArgs}
+	timesCmd := &Command{Use: "times", Run: emptyRun}
+	parentCmd.AddCommand(timesCmd)
+	grandparentCmd.AddCommand(parentCmd)
+	rootCmd.AddCommand(grandparentCmd)
+
+	var expected, output string
+
+	// No typos.
+	expected = ""
+	output, _ = executeCommand(rootCmd, "grandparent")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	expected = ""
+	output, _ = executeCommand(rootCmd, "grandparent", "parent")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	expected = ""
+	output, _ = executeCommand(rootCmd, "grandparent", "parent", "times")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	// 1st level typo.
+	expected = "Error: invalid argument \"grandparen\" for \"root\"\n\nDid you mean this?\n\tgrandparent\n\nUsage:\n  root [flags]\n  root [command]\n\nAvailable Commands:\n  completion  Generate the autocompletion script for the specified shell\n  grandparent \n  help        Help about any command\n\nFlags:\n  -h, --help   help for root\n\nUse \"root [command] --help\" for more information about a command.\n\n"
+	output, _ = executeCommand(rootCmd, "grandparen")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	// 2nd level typo.
+	expected = "Error: invalid argument \"paren\" for \"root grandparent\"\nUsage:\n  root grandparent [flags]\n  root grandparent [command]\n\nAvailable Commands:\n  parent      \n\nFlags:\n  -h, --help   help for grandparent\n\nUse \"root grandparent [command] --help\" for more information about a command.\n\n"
+	output, _ = executeCommand(rootCmd, "grandparent", "paren")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	// 3rd level typo.
+	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
+	output, _ = executeCommand(rootCmd, "grandparent", "parent", "time")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	// Custom suggestion on root function.
+	rootCmd.SetSuggestFunc(func(typedName string) string {
+		return "\nRoot custom suggestion.\n"
+	})
+
+	expected = "Error: invalid argument \"grandparen\" for \"root\"\nRoot custom suggestion.\n\nUsage:\n  root [flags]\n  root [command]\n\nAvailable Commands:\n  completion  Generate the autocompletion script for the specified shell\n  grandparent \n  help        Help about any command\n\nFlags:\n  -h, --help   help for root\n\nUse \"root [command] --help\" for more information about a command.\n\n"
+	output, _ = executeCommand(rootCmd, "grandparen")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nRoot custom suggestion.\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
+	output, _ = executeCommand(rootCmd, "grandparent", "parent", "time")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
+	}
+
+	// Custom suggestion on parent function (kept root's to make sure this one is prioritised).
+	parentCmd.SetSuggestFunc(func(typedName string) string {
+		return "\nParent custom suggestion.\n"
+	})
+
+	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nParent custom suggestion.\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
+	output, _ = executeCommand(rootCmd, "grandparent", "parent", "time")
+	if output != expected {
+		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 }
 

From 4d65edfb548d3f28c33a164d374f997c6dfe142d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= <zan10d@gmail.com>
Date: Sun, 19 Jan 2025 13:05:58 +0100
Subject: [PATCH 4/6] Switch condition and returns.

---
 command.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/command.go b/command.go
index 2b152058..78abf4e2 100644
--- a/command.go
+++ b/command.go
@@ -500,10 +500,10 @@ func (c *Command) SuggestFunc(typedName string) string {
 			if parent.suggestFunc != nil {
 				return parent.suggestFunc
 			}
-			if parent.HasParent() {
-				return getParentFunc(parent.Parent())
+			if !parent.HasParent() {
+				return nil
 			}
-			return nil
+			return getParentFunc(parent.Parent())
 		}
 		parentFunc := getParentFunc(c.Parent())
 		if parentFunc != nil {

From 5897ead2466b52f546c0fd1f784f19328f8654d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= <zan10d@gmail.com>
Date: Mon, 20 Jan 2025 21:15:57 +0100
Subject: [PATCH 5/6] Rework to allow override on suggestion output, only,
 keeping common search logic.

---
 args.go         |  4 +--
 command.go      | 69 +++++++++++++++++++++++--------------------------
 command_test.go | 24 ++++++++---------
 3 files changed, 46 insertions(+), 51 deletions(-)

diff --git a/args.go b/args.go
index c86b1bb4..ed1e70ce 100644
--- a/args.go
+++ b/args.go
@@ -33,7 +33,7 @@ func legacyArgs(cmd *Command, args []string) error {
 
 	// root command with subcommands, do subcommand checking.
 	if !cmd.HasParent() && len(args) > 0 {
-		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.SuggestFunc(args[0]))
+		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
 	}
 	return nil
 }
@@ -58,7 +58,7 @@ func OnlyValidArgs(cmd *Command, args []string) error {
 		}
 		for _, v := range args {
 			if !stringInSlice(v, validArgs) {
-				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.SuggestFunc(args[0]))
+				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
 			}
 		}
 	}
diff --git a/command.go b/command.go
index 78abf4e2..54d7b68d 100644
--- a/command.go
+++ b/command.go
@@ -180,8 +180,8 @@ type Command struct {
 	helpCommand *Command
 	// helpCommandGroupID is the group id for the helpCommand
 	helpCommandGroupID string
-	// suggestFunc is suggest func defined by the user.
-	suggestFunc func(string) string
+	// suggestOutputFunc is user's override for the suggestion output.
+	suggestOutputFunc func([]string) string
 
 	// completionCommandGroupID is the group id for the completion command
 	completionCommandGroupID string
@@ -342,8 +342,8 @@ func (c *Command) SetHelpCommandGroupID(groupID string) {
 	c.helpCommandGroupID = groupID
 }
 
-func (c *Command) SetSuggestFunc(f func(string) string) {
-	c.suggestFunc = f
+func (c *Command) SetSuggestOutputFunc(f func([]string) string) {
+	c.suggestOutputFunc = f
 }
 
 // SetCompletionCommandGroupID sets the group id of the completion command.
@@ -483,37 +483,6 @@ func (c *Command) Help() error {
 	return nil
 }
 
-// SuggestFunc returns suggestions for the provided typedName using either
-// the function set by SetSuggestFunc for this command, parent's or a default one.
-// When searching for a parent's function, it recursively checks towards the root
-// and returns the first one found. If none found, uses direct parent's default.
-func (c *Command) SuggestFunc(typedName string) string {
-	if c.DisableSuggestions {
-		return ""
-	}
-	if c.suggestFunc != nil {
-		return c.suggestFunc(typedName)
-	}
-	if c.HasParent() {
-		var getParentFunc func(*Command) func(string) string
-		getParentFunc = func(parent *Command) func(string) string {
-			if parent.suggestFunc != nil {
-				return parent.suggestFunc
-			}
-			if !parent.HasParent() {
-				return nil
-			}
-			return getParentFunc(parent.Parent())
-		}
-		parentFunc := getParentFunc(c.Parent())
-		if parentFunc != nil {
-			return parentFunc(typedName)
-		}
-		return c.Parent().findSuggestions(typedName)
-	}
-	return c.findSuggestions(typedName)
-}
-
 // UsageString returns usage string.
 func (c *Command) UsageString() string {
 	// Storing normal writers
@@ -786,15 +755,41 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
 	return commandFound, a, nil
 }
 
-func (c *Command) findSuggestions(arg string) string {
+// findSuggestions returns suggestions for the provided typedName if suggestions aren't disabled.
+// The output building function can be overridden by setting it with the SetSuggestOutputFunc.
+// If the output override is, instead, set on a parent, it uses the first one found.
+// If none is set, a default is used.
+func (c *Command) findSuggestions(typedName string) string {
 	if c.DisableSuggestions {
 		return ""
 	}
 	if c.SuggestionsMinimumDistance <= 0 {
 		c.SuggestionsMinimumDistance = 2
 	}
+
+	suggestions := c.SuggestionsFor(typedName)
+
+	if c.suggestOutputFunc != nil {
+		return c.suggestOutputFunc(suggestions)
+	}
+	if c.HasParent() {
+		var getParentFunc func(*Command) func([]string) string
+		getParentFunc = func(parent *Command) func([]string) string {
+			if parent.suggestOutputFunc != nil {
+				return parent.suggestOutputFunc
+			}
+			if !parent.HasParent() {
+				return nil
+			}
+			return getParentFunc(parent.Parent())
+		}
+		if parentFunc := getParentFunc(c.Parent()); parentFunc != nil {
+			return parentFunc(suggestions)
+		}
+	}
+
 	var sb strings.Builder
-	if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
+	if len(suggestions) > 0 {
 		sb.WriteString("\n\nDid you mean this?\n")
 		for _, s := range suggestions {
 			_, _ = fmt.Fprintf(&sb, "\t%v\n", s)
diff --git a/command_test.go b/command_test.go
index e5fad89b..076aa5b7 100644
--- a/command_test.go
+++ b/command_test.go
@@ -1421,11 +1421,11 @@ func TestCustomSuggestions(t *testing.T) {
 	}
 
 	rootCmd.DisableSuggestions = false
-	rootCmd.SetSuggestFunc(func(typedName string) string {
-		return "\nSome custom suggestion.\n"
+	rootCmd.SetSuggestOutputFunc(func(suggestions []string) string {
+		return fmt.Sprintf("\nSuggestions:\n\t%s\n", strings.Join(suggestions, "\n"))
 	})
 
-	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\nSome custom suggestion.\n\nRun 'root --help' for usage.\n", "time")
+	expected = fmt.Sprintf("Error: unknown command \"time\" for \"root\"\nSuggestions:\n\ttimes\n\nRun 'root --help' for usage.\n")
 	output, _ = executeCommand(rootCmd, "time")
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
@@ -1471,42 +1471,42 @@ func TestCustomSuggestions_OnlyValidArgs(t *testing.T) {
 	}
 
 	// 2nd level typo.
-	expected = "Error: invalid argument \"paren\" for \"root grandparent\"\nUsage:\n  root grandparent [flags]\n  root grandparent [command]\n\nAvailable Commands:\n  parent      \n\nFlags:\n  -h, --help   help for grandparent\n\nUse \"root grandparent [command] --help\" for more information about a command.\n\n"
+	expected = "Error: invalid argument \"paren\" for \"root grandparent\"\n\nDid you mean this?\n\tparent\n\nUsage:\n  root grandparent [flags]\n  root grandparent [command]\n\nAvailable Commands:\n  parent      \n\nFlags:\n  -h, --help   help for grandparent\n\nUse \"root grandparent [command] --help\" for more information about a command.\n\n"
 	output, _ = executeCommand(rootCmd, "grandparent", "paren")
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 
 	// 3rd level typo.
-	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
+	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\n\nDid you mean this?\n\ttimes\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
 	output, _ = executeCommand(rootCmd, "grandparent", "parent", "time")
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 
 	// Custom suggestion on root function.
-	rootCmd.SetSuggestFunc(func(typedName string) string {
-		return "\nRoot custom suggestion.\n"
+	rootCmd.SetSuggestOutputFunc(func(suggestions []string) string {
+		return fmt.Sprintf("\nRoot Suggestions:\n\t%s\n", strings.Join(suggestions, "\n"))
 	})
 
-	expected = "Error: invalid argument \"grandparen\" for \"root\"\nRoot custom suggestion.\n\nUsage:\n  root [flags]\n  root [command]\n\nAvailable Commands:\n  completion  Generate the autocompletion script for the specified shell\n  grandparent \n  help        Help about any command\n\nFlags:\n  -h, --help   help for root\n\nUse \"root [command] --help\" for more information about a command.\n\n"
+	expected = "Error: invalid argument \"grandparen\" for \"root\"\nRoot Suggestions:\n\tgrandparent\n\nUsage:\n  root [flags]\n  root [command]\n\nAvailable Commands:\n  completion  Generate the autocompletion script for the specified shell\n  grandparent \n  help        Help about any command\n\nFlags:\n  -h, --help   help for root\n\nUse \"root [command] --help\" for more information about a command.\n\n"
 	output, _ = executeCommand(rootCmd, "grandparen")
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 
-	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nRoot custom suggestion.\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
+	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nRoot Suggestions:\n\ttimes\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
 	output, _ = executeCommand(rootCmd, "grandparent", "parent", "time")
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 
 	// Custom suggestion on parent function (kept root's to make sure this one is prioritised).
-	parentCmd.SetSuggestFunc(func(typedName string) string {
-		return "\nParent custom suggestion.\n"
+	parentCmd.SetSuggestOutputFunc(func(suggestions []string) string {
+		return fmt.Sprintf("\nParent Suggestions:\n\t%s\n", strings.Join(suggestions, "\n"))
 	})
 
-	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nParent custom suggestion.\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
+	expected = "Error: invalid argument \"time\" for \"root grandparent parent\"\nParent Suggestions:\n\ttimes\n\nUsage:\n  root grandparent parent [flags]\n  root grandparent parent [command]\n\nAvailable Commands:\n  times       \n\nFlags:\n  -h, --help   help for parent\n\nUse \"root grandparent parent [command] --help\" for more information about a command.\n\n"
 	output, _ = executeCommand(rootCmd, "grandparent", "parent", "time")
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)

From b81f68ad852c695a67c8d610986929045e829292 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= <zan10d@gmail.com>
Date: Sat, 1 Feb 2025 21:42:11 +0100
Subject: [PATCH 6/6] Fix unnecessary Sprintf.

---
 command_test.go | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/command_test.go b/command_test.go
index 076aa5b7..41815144 100644
--- a/command_test.go
+++ b/command_test.go
@@ -1399,6 +1399,8 @@ func TestCustomSuggestions(t *testing.T) {
 	rootCmd.AddCommand(timesCmd)
 
 	var expected, output string
+	suggestion := "times"
+	typo := "time"
 
 	expected = ""
 	output, _ = executeCommand(rootCmd, "times")
@@ -1406,16 +1408,16 @@ func TestCustomSuggestions(t *testing.T) {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 
-	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n", "time", "times")
-	output, _ = executeCommand(rootCmd, "time")
+	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n", typo, suggestion)
+	output, _ = executeCommand(rootCmd, typo)
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
 
 	rootCmd.DisableSuggestions = true
 
-	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n", "time")
-	output, _ = executeCommand(rootCmd, "time")
+	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n", typo)
+	output, _ = executeCommand(rootCmd, typo)
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}
@@ -1425,8 +1427,8 @@ func TestCustomSuggestions(t *testing.T) {
 		return fmt.Sprintf("\nSuggestions:\n\t%s\n", strings.Join(suggestions, "\n"))
 	})
 
-	expected = fmt.Sprintf("Error: unknown command \"time\" for \"root\"\nSuggestions:\n\ttimes\n\nRun 'root --help' for usage.\n")
-	output, _ = executeCommand(rootCmd, "time")
+	expected = fmt.Sprintf("Error: unknown command \"%s\" for \"root\"\nSuggestions:\n\t%s\n\nRun 'root --help' for usage.\n", typo, suggestion)
+	output, _ = executeCommand(rootCmd, typo)
 	if output != expected {
 		t.Errorf("\nExpected:\n %q\nGot:\n %q", expected, output)
 	}