From c6b1bd554649b43c05886b0188a6e201d54a2a92 Mon Sep 17 00:00:00 2001 From: Gabriele Belluardo Date: Sat, 28 Mar 2026 20:47:28 +0100 Subject: [PATCH] feat: add descriptions for bash and powershell autocompletion --- autocomplete/bash_autocomplete | 35 +++++++++++++++++++++++- autocomplete/powershell_autocomplete.ps1 | 17 ++++++++---- examples_test.go | 14 +++------- help.go | 6 ++-- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index d63937d971..987314422a 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -26,7 +26,40 @@ __%[1]s_bash_autocomplete() { requestComp="${words[*]} --generate-shell-completion" fi opts=$(eval "${requestComp}" 2>/dev/null) - COMPREPLY=($(compgen -W "${opts}" -- ${cur})) + + # Separate completions and descriptions + local completions=() + local descriptions=() + local longest=0 + while IFS=$'\n' read -r line; do + local comp_part desc_part + if [[ "$line" == *:* ]]; then + comp_part="${line%%:*}" + desc_part="${line#*:}" + else + comp_part="$line" + desc_part="" + fi + completions+=("$comp_part") + descriptions+=("$desc_part") + (( ${#comp_part} > longest )) && longest=${#comp_part} + done <<< "$opts" + + # Format completions with aligned descriptions + for i in "${!completions[@]}"; do + local padded_completion="${completions[i]}" + local pad_len=$((longest - ${#padded_completion})) + if (( pad_len > 0 )); then + padded_completion="${padded_completion}$(head -c $pad_len < /dev/zero | tr '\0' ' ')" + fi + + if [[ -n "${descriptions[i]}" ]]; then + COMPREPLY+=("${padded_completion} -- ${descriptions[i]}") + else + COMPREPLY+=("${padded_completion}") + fi + done + return 0 fi } diff --git a/autocomplete/powershell_autocomplete.ps1 b/autocomplete/powershell_autocomplete.ps1 index 6e0c422e25..fee6d0c7d2 100644 --- a/autocomplete/powershell_autocomplete.ps1 +++ b/autocomplete/powershell_autocomplete.ps1 @@ -1,9 +1,16 @@ $fn = $($MyInvocation.MyCommand.Name) $name = $fn -replace "(.*)\.ps1$", '$1' Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock { - param($commandName, $wordToComplete, $cursorPosition) - $other = "$wordToComplete --generate-shell-completion" - Invoke-Expression $other | ForEach-Object { + param($commandName, $wordToComplete, $cursorPosition) + $other = "$wordToComplete --generate-shell-completion" + Invoke-Expression $other | ForEach-Object { + $parts = $_.Split(':', 2) + if ($parts.Count -eq 2) { + $completion = $parts[0].Trim() + $description = $parts[1].Trim() + [System.Management.Automation.CompletionResult]::new($completion, $completion, 'ParameterValue', $description) + } else { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) - } - } + } + } +} \ No newline at end of file diff --git a/examples_test.go b/examples_test.go index afda009ceb..d5aa1ef2c6 100644 --- a/examples_test.go +++ b/examples_test.go @@ -258,14 +258,13 @@ func ExampleCommand_Run_shellComplete_bash_withShortFlag() { } // Simulate a bash environment and command line arguments - os.Setenv("SHELL", "bash") os.Args = []string{"greet", "-", "--generate-shell-completion"} _ = cmd.Run(context.Background(), os.Args) // Output: // --other // --xyz - // --help + // --help:show help } func ExampleCommand_Run_shellComplete_bash_withLongFlag() { @@ -291,7 +290,6 @@ func ExampleCommand_Run_shellComplete_bash_withLongFlag() { } // Simulate a bash environment and command line arguments - os.Setenv("SHELL", "bash") os.Args = []string{"greet", "--s", "--generate-shell-completion"} _ = cmd.Run(context.Background(), os.Args) @@ -326,7 +324,6 @@ func ExampleCommand_Run_shellComplete_bash_withMultipleLongFlag() { } // Simulate a bash environment and command line arguments - os.Setenv("SHELL", "bash") os.Args = []string{"greet", "--st", "--generate-shell-completion"} _ = cmd.Run(context.Background(), os.Args) @@ -362,14 +359,13 @@ func ExampleCommand_Run_shellComplete_bash() { } // Simulate a bash environment and command line arguments - os.Setenv("SHELL", "bash") os.Args = []string{"greet", "--generate-shell-completion"} _ = cmd.Run(context.Background(), os.Args) // Output: - // describeit - // next - // help + // describeit:use it to see a description + // next:next example + // help:Shows a list of commands or help for one command } func ExampleCommand_Run_shellComplete_zsh() { @@ -400,7 +396,6 @@ func ExampleCommand_Run_shellComplete_zsh() { // Simulate a zsh environment and command line arguments os.Args = []string{"greet", "--generate-shell-completion"} - os.Setenv("SHELL", "/usr/bin/zsh") _ = cmd.Run(context.Background(), os.Args) // Output: @@ -437,7 +432,6 @@ func ExampleCommand_Run_shellComplete_fish() { // Simulate a fish environment and command line arguments os.Args = []string{"greet", "--generate-shell-completion"} - os.Setenv("SHELL", "/usr/bin/fish") _ = cmd.Run(context.Background(), os.Args) // Output: diff --git a/help.go b/help.go index 0f7f629caf..582544cc0e 100644 --- a/help.go +++ b/help.go @@ -184,12 +184,11 @@ func DefaultRootCommandComplete(ctx context.Context, cmd *Command) { var DefaultAppComplete = DefaultRootCommandComplete func printCommandSuggestions(commands []*Command, writer io.Writer) { - shell := os.Getenv("SHELL") for _, command := range commands { if command.Hidden { continue } - if (strings.HasSuffix(shell, "zsh") || strings.HasSuffix(shell, "fish")) && len(command.Usage) > 0 { + if len(command.Usage) > 0 { _, _ = fmt.Fprintf(writer, "%s:%s\n", command.Name, command.Usage) } else { _, _ = fmt.Fprintf(writer, "%s\n", command.Name) @@ -239,8 +238,7 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name /* && !cliArgContains(name, os.Args)*/ { flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - shell := os.Getenv("SHELL") - if usage != "" && (strings.HasSuffix(shell, "zsh") || strings.HasSuffix(shell, "fish")) { + if usage != "" { flagCompletion = fmt.Sprintf("%s:%s", flagCompletion, usage) } fmt.Fprintln(writer, flagCompletion)