Complete hidden commands flags and its subcommands

This change enable to have a correct completion on hidden commands flags and
its subcommands if present, when using bash.
If an exact hidden command is typed on the command line and then the
user request completion, instead of printing other available subcommand
at the same level (which is thus incorrect), it will print any of its
subcommand if available.
Flags completion request (starting with -) will also work as expected,
as well as flag completion request on any subcommand (persistent flags
or local).
This commit is contained in:
Didier Roche 2019-10-25 17:30:57 +02:00
parent 77e4d5aecc
commit ae5480b1f8
2 changed files with 36 additions and 4 deletions

View file

@ -190,6 +190,7 @@ __%[1]s_handle_flag()
# if you set a flag which only applies to this command, don't show subcommands # if you set a flag which only applies to this command, don't show subcommands
if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
commands=() commands=()
hidden_commands=()
fi fi
# keep flag value with flagname as flaghash # keep flag value with flagname as flaghash
@ -211,6 +212,7 @@ __%[1]s_handle_flag()
# if we are looking for a flags value, don't show commands # if we are looking for a flags value, don't show commands
if [[ $c -eq $cword ]]; then if [[ $c -eq $cword ]]; then
commands=() commands=()
hidden_commands=()
fi fi
fi fi
@ -262,6 +264,8 @@ __%[1]s_handle_word()
__%[1]s_handle_flag __%[1]s_handle_flag
elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then
__%[1]s_handle_command __%[1]s_handle_command
elif __%[1]s_contains_word "${words[c]}" "${hidden_commands[@]}"; then
__%[1]s_handle_command
elif [[ $c -eq 0 ]]; then elif [[ $c -eq 0 ]]; then
__%[1]s_handle_command __%[1]s_handle_command
elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then
@ -323,10 +327,14 @@ fi
func writeCommands(buf *bytes.Buffer, cmd *Command) { func writeCommands(buf *bytes.Buffer, cmd *Command) {
buf.WriteString(" commands=()\n") buf.WriteString(" commands=()\n")
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c == cmd.helpCommand { if (!c.IsAvailableCommand() && !c.Hidden) || c == cmd.helpCommand {
continue continue
} }
buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) commands := "commands"
if c.Hidden {
commands = "hidden_commands"
}
buf.WriteString(fmt.Sprintf(" %s+=(%q)\n", commands, c.Name()))
writeCmdAliases(buf, c) writeCmdAliases(buf, c)
} }
buf.WriteString("\n") buf.WriteString("\n")
@ -495,7 +503,7 @@ func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
func gen(buf *bytes.Buffer, cmd *Command) { func gen(buf *bytes.Buffer, cmd *Command) {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c == cmd.helpCommand { if (!c.IsAvailableCommand() && !c.Hidden) || c == cmd.helpCommand {
continue continue
} }
gen(buf, c) gen(buf, c)

View file

@ -130,6 +130,22 @@ func TestBashCompletions(t *testing.T) {
Run: emptyRun, Run: emptyRun,
} }
hiddenCmd := &Command{
Use: "hidden",
Short: "A command which is hidden",
Long: "an absolutely utterly useless command for testing for testing hiding.",
Hidden: true,
Run: emptyRun,
}
hiddenSubCmd := &Command{
Use: "subcommandForHidden",
Short: "A command which is attached to a hidden one",
Long: "an absolutely utterly useless command for testing for testing subcommand attached to hidden one.",
Hidden: true,
Run: emptyRun,
}
colonCmd := &Command{ colonCmd := &Command{
Use: "cmd:colon", Use: "cmd:colon",
Run: emptyRun, Run: emptyRun,
@ -146,7 +162,8 @@ func TestBashCompletions(t *testing.T) {
} }
echoCmd.AddCommand(timesCmd) echoCmd.AddCommand(timesCmd)
rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd) hiddenCmd.AddCommand(hiddenSubCmd)
rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, hiddenCmd, colonCmd)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
rootCmd.GenBashCompletion(buf) rootCmd.GenBashCompletion(buf)
@ -195,6 +212,13 @@ func TestBashCompletions(t *testing.T) {
checkOmit(t, output, deprecatedCmd.Name()) checkOmit(t, output, deprecatedCmd.Name())
// check that hidden command and its subcommand functions are available
check(t, output, "_root_hidden")
check(t, output, "_root_hidden_subcommandForHidden")
checkOmit(t, output, ` commands+=("hidden")`)
check(t, output, `hidden_commands+=("hidden")`)
check(t, output, `commands+=("subcommandForHidden")`)
// If available, run shellcheck against the script. // If available, run shellcheck against the script.
if err := exec.Command("which", "shellcheck").Run(); err != nil { if err := exec.Command("which", "shellcheck").Run(); err != nil {
return return