diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 9b3d9262e..adff0f641 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -45,6 +45,7 @@ var ( "zsh": runCompletionZsh, "fish": runCompletionFish, } + completionNoDesc bool ) func newCompletionCmd(out io.Writer) *cobra.Command { @@ -62,6 +63,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { }, ValidArgs: shells, } + cmd.PersistentFlags().BoolVar(&completionNoDesc, "no-descriptions", false, "disable completion description for shells that support it") return cmd } @@ -249,6 +251,10 @@ __helm_bash_source <(__helm_convert_bash_to_zsh) } func runCompletionFish(out io.Writer, cmd *cobra.Command) error { + compCmd := completion.CompWithDescRequestCmd + if completionNoDesc { + compCmd = completion.CompRequestCmd + } fishScript := fmt.Sprintf(`# fish completion for helm -*- shell-script -*- function __helm_debug @@ -272,7 +278,7 @@ function __helm_handle_completion end __helm_debug "emptyArg: $emptyArg" - set requestComp "$args[1] __complete $args[2..-1] $emptyArg" + set requestComp "$args[1] %[1]s $args[2..-1] $emptyArg" __helm_debug "Calling $requestComp" eval $requestComp 2> /dev/null end @@ -289,13 +295,13 @@ function __helm_get_completions __helm_debug "Completions are: $comps" __helm_debug "Directive is: $directive" - set -l compErr (math (math $directive / %[1]d) %% 2) + set -l compErr (math (math $directive / %[2]d) %% 2) if test $compErr -eq 1 return 0 end - set -l nospace (math (math $directive / %[2]d) %% 2) - set -l nofiles (math (math $directive / %[3]d) %% 2) + set -l nospace (math (math $directive / %[3]d) %% 2) + set -l nofiles (math (math $directive / %[4]d) %% 2) __helm_debug "nospace: $nospace, nofiles: $nofiles" @@ -325,7 +331,7 @@ complete -c helm -n 'not __helm_get_completions' # This completion will be run first as complete commands are added FILO. It first clears the cache. complete -c helm -n 'set -e __helm_cache_completions; __helm_get_completions' -f -a '(__helm_get_completions)' -`, completion.BashCompDirectiveError, completion.BashCompDirectiveNoSpace, completion.BashCompDirectiveNoFileComp) +`, compCmd, completion.BashCompDirectiveError, completion.BashCompDirectiveNoSpace, completion.BashCompDirectiveNoFileComp) out.Write([]byte(fishScript)) return nil diff --git a/internal/completion/complete.go b/internal/completion/complete.go index 16ee032b5..15c82d639 100644 --- a/internal/completion/complete.go +++ b/internal/completion/complete.go @@ -35,9 +35,14 @@ import ( // This should ultimately be pushed down into Cobra. // ================================================================================== -// CompRequestCmd is the name of the hidden command that is used to request -// completion results from helm. It is used by the shell completion script. -const CompRequestCmd = "__complete" +const ( + // CompRequestCmd is the name of the hidden command that is used to request + // completion results from helm. It is used by the shell completion script. + CompRequestCmd = "__complete" + // CompWithDescRequestCmd is the name of the hidden command that is used to request + // completion results with their description. It is used by the shell completion script. + CompWithDescRequestCmd = "__completeD" +) // Global map allowing to find completion functions for commands or flags. var validArgsFunctions = map[interface{}]func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective){} @@ -181,12 +186,13 @@ func NewCompleteCmd(settings *cli.EnvSettings, out io.Writer) *cobra.Command { DisableFlagsInUseLine: true, Hidden: true, DisableFlagParsing: true, + Aliases: []string{CompWithDescRequestCmd}, Args: require.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.", CompRequestCmd), Run: func(cmd *cobra.Command, args []string) { - completions, directive, err := getCompletions(cmd.Root(), args) + completions, directive, err := getCompletions(cmd, args) if err != nil { CompErrorln(err.Error()) // Keep going for multiple reasons: @@ -215,7 +221,7 @@ func NewCompleteCmd(settings *cli.EnvSettings, out io.Writer) *cobra.Command { } } -func getCompletions(rootCmd *cobra.Command, args []string) ([]string, BashCompDirective, error) { +func getCompletions(cmd *cobra.Command, args []string) ([]string, BashCompDirective, error) { var completions []string // The last argument, which is not completely typed by the user, @@ -224,19 +230,20 @@ func getCompletions(rootCmd *cobra.Command, args []string) ([]string, BashCompDi trimmedArgs := args[:len(args)-1] // Find the real command for which completion must be performed - finalCmd, finalArgs, err := rootCmd.Find(trimmedArgs) + finalCmd, finalArgs, err := cmd.Root().Find(trimmedArgs) if err != nil { // Unable to find the real command. E.g., helm invalidCmd return completions, BashCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs) } + includeDesc := (cmd.CalledAs() == CompWithDescRequestCmd) if isFlag(toComplete) && !strings.Contains(toComplete, "=") { // We are completing a flag name finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { - completions = append(completions, getFlagNameCompletions(flag, toComplete)...) + completions = append(completions, getFlagNameCompletions(flag, toComplete, includeDesc)...) }) finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { - completions = append(completions, getFlagNameCompletions(flag, toComplete)...) + completions = append(completions, getFlagNameCompletions(flag, toComplete, includeDesc)...) }) directive := BashCompDirectiveDefault @@ -263,7 +270,11 @@ func getCompletions(rootCmd *cobra.Command, args []string) ([]string, BashCompDi // Complete subcommand names for _, subCmd := range finalCmd.Commands() { if subCmd.IsAvailableCommand() && strings.HasPrefix(subCmd.Name(), toComplete) { - completions = append(completions, subCmd.Name()) + comp := subCmd.Name() + if includeDesc { + comp = fmt.Sprintf("%s\t%s", comp, subCmd.Short) + } + completions = append(completions, comp) } } @@ -313,7 +324,7 @@ func getCompletions(rootCmd *cobra.Command, args []string) ([]string, BashCompDi return completions, directive, nil } -func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { +func getFlagNameCompletions(flag *pflag.Flag, toComplete string, includeDesc bool) []string { if nonCompletableFlag(flag) { return []string{} } @@ -335,6 +346,13 @@ func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { if len(flag.Shorthand) > 0 && strings.HasPrefix(comp, toComplete) { completions = append(completions, comp) } + + // Add documentation if requested + if includeDesc { + for idx, comp := range completions { + completions[idx] = fmt.Sprintf("%s\t%s", comp, flag.Usage) + } + } return completions }