Incorporating pull request feedback.

- Renamed completer to be cobra_completer to match docs
- Added whitespace after each completion
- Implemented ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp
- Disabled active help as it isn't supported by nushell
- Added nushell to the default completion command
This commit is contained in:
Jack Wright 2022-11-27 13:46:13 -08:00
parent 594faef23f
commit 2f276e3014
4 changed files with 358 additions and 159 deletions

View File

@ -836,14 +836,47 @@ to your powershell profile.
return cmd.Root().GenPowerShellCompletion(out) return cmd.Root().GenPowerShellCompletion(out)
} }
return cmd.Root().GenPowerShellCompletionWithDesc(out) return cmd.Root().GenPowerShellCompletionWithDesc(out)
}, },
} }
if haveNoDescFlag { if haveNoDescFlag {
powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc) powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
} }
completionCmd.AddCommand(bash, zsh, fish, powershell) nushell := &Command{
Use: "nushell",
Short: fmt.Sprintf(shortDesc, "nushell"),
Long: fmt.Sprintf(`Generate the autocompletion script for nushell.
To configure completions:
# 1. Copy the output of the command below:
> %[1]s completion nushell
# 2. Edit the nushell config file:
> config nu
# 3. Paste above the "let-env config" line.
# 4. Change the config block's external_completer line to be
external_completer: $cobra_completer
# 5. You will need to start a new shell for this setup to take effect.
`, c.Root().Name()),
Args: NoArgs,
ValidArgsFunction: NoFileCompletions,
RunE: func(cmd *Command, args []string) error {
if noDesc {
return cmd.Root().GenPowerShellCompletion(out)
}
return cmd.Root().GenPowerShellCompletionWithDesc(out)
},
}
if haveNoDescFlag {
nushell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
}
completionCmd.AddCommand(bash, zsh, fish, powershell, nushell)
} }
func findFlag(cmd *Command, name string) *pflag.Flag { func findFlag(cmd *Command, name string) *pflag.Flag {
@ -876,7 +909,7 @@ func CompDebug(msg string, printToStdErr bool) {
// variable BASH_COMP_DEBUG_FILE to the path of some file to be used. // variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" { if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" {
f, err := os.OpenFile(path, f, err := os.OpenFile(path,
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err == nil { if err == nil {
defer f.Close() defer f.Close()
WriteStringAndCheck(f, msg) WriteStringAndCheck(f, msg)

File diff suppressed because it is too large Load Diff

View File

@ -16,31 +16,39 @@ package cobra
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"os" "os"
) )
func (c *Command) GenNushellCompletion(w io.Writer) error { func (c *Command) GenNushellCompletion(w io.Writer) error {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
WriteStringAndCheck(buf, ` WriteStringAndCheck(buf, fmt.Sprintf(`
# An external configurator that works with any cobra based # An external configurator that works with any cobra based
# command line application (e.g. kubectl, minikube) # command line application (e.g. kubectl, minikube)
let cobra_configurator = {|spans| let cobra_completer = {|spans|
let ShellCompDirectiveError = %[1]d
let ShellCompDirectiveNoSpace = %[2]d
let ShellCompDirectiveNoFileComp = %[3]d
let ShellCompDirectiveFilterFileExt = %[4]d
let ShellCompDirectiveFilterDirs = %[5]d
let cmd = $spans.0 let cmd = $spans.0
let last_span = ($spans | last | str trim)
# skip the first entry in the span (the command) and join the rest of the span to create __complete args # skip the first entry in the span (the command) and join the rest of the span to create __complete args
let cmd_args = ($spans | skip 1 | str join ' ') let cmd_args = ($spans | skip 1 | str join ' ')
# If the last span entry was empty add "" to the end of the command args # If the last span entry was empty add "" to the end of the command args
let cmd_args = if ($spans | last | str trim | is-empty) { let cmd_args = if ($last_span | is-empty) {
$'($cmd_args) ""' $'($cmd_args) ""'
} else { } else {
$cmd_args $cmd_args
} }
# The full command to be executed # The full command to be executed with active help disable (Nushell does not support active help)
let full_cmd = $'($cmd) __complete ($cmd_args)' let full_cmd = $'($cmd)_ACTIVE_HELP=0 ($cmd) __complete ($cmd_args)'
# Since nushell doesn't have anything like eval, execute in a subshell # Since nushell doesn't have anything like eval, execute in a subshell
let result = (do -i { nu -c $"'($full_cmd)'" } | complete) let result = (do -i { nu -c $"'($full_cmd)'" } | complete)
@ -48,16 +56,33 @@ let cobra_configurator = {|spans|
# Create a record with all completion related info. # Create a record with all completion related info.
# directive and directive_str are for posterity # directive and directive_str are for posterity
let stdout_lines = ($result.stdout | lines) let stdout_lines = ($result.stdout | lines)
let $completions = ($stdout_lines | drop | parse -r '([\w\-\.:\+]*)\t?(.*)' | rename value description) let directive = ($stdout_lines | last | str trim | str replace ":" "" | into int)
let completions = ($stdout_lines | drop | parse -r '([\w\-\.:\+\=]*)\t?(.*)' | rename value description)
let result = ({ # Add space at the end of each completion
completions: $completions let completions = if $directive != $ShellCompDirectiveNoSpace {
directive_str: ($result.stderr) ($completions | each {|it| {value: $"($it.value) ", description: $it.description}})
directive: ($stdout_lines | last) } else {
}) $completions
}
$result.completions if $last_span =~ '=$' {
}`) # return flag as part of the completion so that it doesn't get replaced
$completions | each {|it| $"($last_span)($it.value)" }
} else if $directive == $ShellCompDirectiveNoFileComp {
# Allow empty results as this will stop file completion
$completions
} else if ($completions | is-empty) or $directive == $ShellCompDirectiveError {
# Not returning null causes file completions to break
# Return null if there are no completions or ShellCompDirectiveError
null
} else {
$completions
}
}
`, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
_, err := buf.WriteTo(w) _, err := buf.WriteTo(w)
return err return err

View File

@ -38,7 +38,7 @@ func TestGenNushellCompletion(t *testing.T) {
assertNoErr(t, rootCmd.GenNushellCompletion(buf)) assertNoErr(t, rootCmd.GenNushellCompletion(buf))
output := buf.String() output := buf.String()
check(t, output, "let full_cmd = $'($cmd) __complete ($cmd_args)'") check(t, output, "let full_cmd = $'($cmd)_ACTIVE_HELP=0 ($cmd) __complete ($cmd_args)'")
} }
func TestGenNushellCompletionFile(t *testing.T) { func TestGenNushellCompletionFile(t *testing.T) {