Skip to content

Commit 0431872

Browse files
marckhouzamvoithos
andauthored
Add completion for help command (#1136)
* Don't exclude 'help' from bash completions Fixes #1000. * Provide completion for the help command 1- Show 'help' as a possible completion 2- Provide completions for the help command itself Signed-off-by: Marc Khouzam <[email protected]> Co-authored-by: Zaven Muradyan <[email protected]>
1 parent ed7b60e commit 0431872

4 files changed

+94
-6
lines changed

bash_completions.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ fi
389389
func writeCommands(buf *bytes.Buffer, cmd *Command) {
390390
buf.WriteString(" commands=()\n")
391391
for _, c := range cmd.Commands() {
392-
if !c.IsAvailableCommand() || c == cmd.helpCommand {
392+
if !c.IsAvailableCommand() && c != cmd.helpCommand {
393393
continue
394394
}
395395
buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name()))
@@ -582,7 +582,7 @@ func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
582582

583583
func gen(buf *bytes.Buffer, cmd *Command) {
584584
for _, c := range cmd.Commands() {
585-
if !c.IsAvailableCommand() || c == cmd.helpCommand {
585+
if !c.IsAvailableCommand() && c != cmd.helpCommand {
586586
continue
587587
}
588588
gen(buf, c)

command.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,25 @@ func (c *Command) InitDefaultHelpCmd() {
10561056
Short: "Help about any command",
10571057
Long: `Help provides help for any command in the application.
10581058
Simply type ` + c.Name() + ` help [path to command] for full details.`,
1059-
1059+
ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
1060+
var completions []string
1061+
cmd, _, e := c.Root().Find(args)
1062+
if e != nil {
1063+
return nil, ShellCompDirectiveNoFileComp
1064+
}
1065+
if cmd == nil {
1066+
// Root help command.
1067+
cmd = c.Root()
1068+
}
1069+
for _, subCmd := range cmd.Commands() {
1070+
if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand {
1071+
if strings.HasPrefix(subCmd.Name(), toComplete) {
1072+
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
1073+
}
1074+
}
1075+
}
1076+
return completions, ShellCompDirectiveNoFileComp
1077+
},
10601078
Run: func(c *Command, args []string) {
10611079
cmd, _, e := c.Root().Find(args)
10621080
if cmd == nil || e != nil {

custom_completions.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,12 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
183183
}
184184

185185
if flag == nil {
186-
// Complete subcommand names
186+
// Complete subcommand names, including the help command
187187
for _, subCmd := range finalCmd.Commands() {
188-
if subCmd.IsAvailableCommand() && strings.HasPrefix(subCmd.Name(), toComplete) {
189-
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
188+
if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand {
189+
if strings.HasPrefix(subCmd.Name(), toComplete) {
190+
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
191+
}
190192
}
191193
}
192194

custom_completions_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -629,3 +629,71 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
629629
t.Errorf("expected: %q, got: %q", expected, output)
630630
}
631631
}
632+
633+
func TestCompleteHelp(t *testing.T) {
634+
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
635+
child1Cmd := &Command{
636+
Use: "child1",
637+
Run: emptyRun,
638+
}
639+
child2Cmd := &Command{
640+
Use: "child2",
641+
Run: emptyRun,
642+
}
643+
rootCmd.AddCommand(child1Cmd, child2Cmd)
644+
645+
child3Cmd := &Command{
646+
Use: "child3",
647+
Run: emptyRun,
648+
}
649+
child1Cmd.AddCommand(child3Cmd)
650+
651+
// Test that completion includes the help command
652+
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
653+
if err != nil {
654+
t.Errorf("Unexpected error: %v", err)
655+
}
656+
657+
expected := strings.Join([]string{
658+
"child1",
659+
"child2",
660+
"help",
661+
":0",
662+
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
663+
664+
if output != expected {
665+
t.Errorf("expected: %q, got: %q", expected, output)
666+
}
667+
668+
// Test sub-commands are completed on first level of help command
669+
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "")
670+
if err != nil {
671+
t.Errorf("Unexpected error: %v", err)
672+
}
673+
674+
expected = strings.Join([]string{
675+
"child1",
676+
"child2",
677+
"help", // "<program> help help" is a valid command, so should be completed
678+
":4",
679+
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
680+
681+
if output != expected {
682+
t.Errorf("expected: %q, got: %q", expected, output)
683+
}
684+
685+
// Test sub-commands are completed on first level of help command
686+
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "")
687+
if err != nil {
688+
t.Errorf("Unexpected error: %v", err)
689+
}
690+
691+
expected = strings.Join([]string{
692+
"child3",
693+
":4",
694+
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
695+
696+
if output != expected {
697+
t.Errorf("expected: %q, got: %q", expected, output)
698+
}
699+
}

0 commit comments

Comments
 (0)