mirror of
https://github.com/spf13/cobra
synced 2025-05-07 13:57:21 +00:00
Use the empty string as a parameter to the flag being completed
Also attempt to standardize the bash and zsh implementations Next up: tests!
This commit is contained in:
parent
53e4e28648
commit
ee1287cc3f
4 changed files with 55 additions and 44 deletions
|
@ -280,7 +280,12 @@ __%[1]s_handle_word()
|
|||
__%[1]s_handle_word
|
||||
}
|
||||
|
||||
__%[1]s_handle_dynamic_flag_completion()
|
||||
`, name))
|
||||
}
|
||||
|
||||
func writeDynamicFlagCompletionFunction(buf *bytes.Buffer, dynamicFlagCompletionFunc string) {
|
||||
buf.WriteString(fmt.Sprintf(`
|
||||
%s()
|
||||
{
|
||||
export COBRA_FLAG_COMPLETION="$1"
|
||||
|
||||
|
@ -289,7 +294,7 @@ __%[1]s_handle_dynamic_flag_completion()
|
|||
return $?
|
||||
fi
|
||||
|
||||
if ! error_message="$($COMP_LINE > "$output")" ; then
|
||||
if ! error_message="$("${COMP_WORDS[@]}" > "$output")" ; then
|
||||
local st="$?"
|
||||
echo "$error_message"
|
||||
return "$st"
|
||||
|
@ -303,7 +308,7 @@ __%[1]s_handle_dynamic_flag_completion()
|
|||
rm "$output"
|
||||
}
|
||||
|
||||
`, name))
|
||||
`, dynamicFlagCompletionFunc))
|
||||
}
|
||||
|
||||
func writePostscript(buf *bytes.Buffer, name string) {
|
||||
|
@ -553,17 +558,19 @@ func gen(buf *bytes.Buffer, cmd *Command) {
|
|||
|
||||
// GenBashCompletion generates bash completion file and writes to the passed writer.
|
||||
func (c *Command) GenBashCompletion(w io.Writer) error {
|
||||
visitAllFlagsWithCompletions(c, func(f *pflag.Flag) {
|
||||
if f.Annotations == nil {
|
||||
f.Annotations = make(map[string][]string)
|
||||
}
|
||||
f.Annotations[BashCompDynamic] = []string{"__" + c.Root().Name() + "_handle_dynamic_flag_completion " + f.Name}
|
||||
dynamicFlagCompletionFunc := "__" + c.Root().Name() + "_handle_dynamic_flag_completion"
|
||||
c.Root().visitAllFlagsWithCompletions(func(f *pflag.Flag) {
|
||||
f.Annotations[BashCompDynamic] = []string{dynamicFlagCompletionFunc + " " + f.Name}
|
||||
})
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
writePreamble(buf, c.Name())
|
||||
if len(c.BashCompletionFunction) > 0 {
|
||||
buf.WriteString(c.BashCompletionFunction + "\n")
|
||||
}
|
||||
if c.HasDynamicCompletions() {
|
||||
writeDynamicFlagCompletionFunction(buf, dynamicFlagCompletionFunc)
|
||||
}
|
||||
gen(buf, c)
|
||||
writePostscript(buf, c.Name())
|
||||
|
||||
|
|
29
command.go
29
command.go
|
@ -212,9 +212,9 @@ type Command struct {
|
|||
// errWriter is a writer defined by the user that replaces stderr
|
||||
errWriter io.Writer
|
||||
|
||||
// flagCompletions is a map of flag to a function that returns a list of values to suggest during tab completion for
|
||||
// this flag
|
||||
flagCompletions map[*flag.Flag]DynamicFlagCompletion
|
||||
// dynamicFlagCompletions is a map of flag to a function that returns a list of values to suggest during tab
|
||||
// completion for this flag
|
||||
dynamicFlagCompletions map[*flag.Flag]DynamicFlagCompletion
|
||||
}
|
||||
|
||||
// Context returns underlying command context. If command wasn't
|
||||
|
@ -907,7 +907,6 @@ func (c *Command) complete(flagName string, a []string) (err error) {
|
|||
} else {
|
||||
c.Flags().StringVar(¤tCompletionValue, flagName, "", "")
|
||||
}
|
||||
c.Flag(flagName).NoOptDefVal = "_hack_"
|
||||
|
||||
err = c.ParseFlags(a)
|
||||
if err != nil {
|
||||
|
@ -917,10 +916,10 @@ func (c *Command) complete(flagName string, a []string) (err error) {
|
|||
c.preRun()
|
||||
|
||||
currentCommand := c
|
||||
completionFunc := currentCommand.flagCompletions[flagToComplete]
|
||||
completionFunc := currentCommand.dynamicFlagCompletions[flagToComplete]
|
||||
for completionFunc == nil && currentCommand.HasParent() {
|
||||
currentCommand = currentCommand.Parent()
|
||||
completionFunc = currentCommand.flagCompletions[flagToComplete]
|
||||
completionFunc = currentCommand.dynamicFlagCompletions[flagToComplete]
|
||||
}
|
||||
if completionFunc == nil {
|
||||
return fmt.Errorf("%s does not have completions enabled", flagName)
|
||||
|
@ -958,6 +957,7 @@ func (c *Command) complete(flagName string, a []string) (err error) {
|
|||
}
|
||||
|
||||
for _, v := range values {
|
||||
c.OutOrStdout()
|
||||
fmt.Print(v + "\x00")
|
||||
}
|
||||
|
||||
|
@ -1741,15 +1741,26 @@ func (c *Command) updateParentsPflags() {
|
|||
})
|
||||
}
|
||||
|
||||
func visitAllFlagsWithCompletions(c *Command, fn func(*flag.Flag)) {
|
||||
func (c *Command) HasDynamicCompletions() bool {
|
||||
hasCompletions := false
|
||||
c.visitAllFlagsWithCompletions(func(*flag.Flag) { hasCompletions = true })
|
||||
return hasCompletions
|
||||
}
|
||||
|
||||
// visitAllFlagsWithCompletions recursively visits all flags and persistent flags that have dynamic completions enabled.
|
||||
// Initializes the flag's Annotations map if nil before calling fn
|
||||
func (c Command) visitAllFlagsWithCompletions(fn func(*flag.Flag)) {
|
||||
filterFunc := func(f *flag.Flag) {
|
||||
if _, ok := c.flagCompletions[f]; ok {
|
||||
if _, ok := c.dynamicFlagCompletions[f]; ok {
|
||||
if f.Annotations == nil {
|
||||
f.Annotations = make(map[string][]string)
|
||||
}
|
||||
fn(f)
|
||||
}
|
||||
}
|
||||
c.Flags().VisitAll(filterFunc)
|
||||
c.PersistentFlags().VisitAll(filterFunc)
|
||||
for _, sc := range c.Commands() {
|
||||
visitAllFlagsWithCompletions(sc, fn)
|
||||
sc.visitAllFlagsWithCompletions(fn)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,10 +103,10 @@ func (c *Command) MarkDynamicFlagCompletion(name string, completion DynamicFlagC
|
|||
return fmt.Errorf("%s takes no parameters", name)
|
||||
}
|
||||
|
||||
if c.flagCompletions == nil {
|
||||
c.flagCompletions = make(map[*pflag.Flag]DynamicFlagCompletion)
|
||||
if c.dynamicFlagCompletions == nil {
|
||||
c.dynamicFlagCompletions = make(map[*pflag.Flag]DynamicFlagCompletion)
|
||||
}
|
||||
c.flagCompletions[flag] = completion
|
||||
c.dynamicFlagCompletions[flag] = completion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ var (
|
|||
"genFlagEntryForZshArguments": zshCompGenFlagEntryForArguments,
|
||||
"extractArgsCompletions": zshCompExtractArgumentCompletionHintsForRendering,
|
||||
"genZshFlagDynamicCompletionFuncName": zshCompGenDynamicFlagCompletionFuncName,
|
||||
"hasDynamicCompletions": zshCompHasDynamicCompletions,
|
||||
}
|
||||
zshCompletionText = `
|
||||
{{/* should accept Command (that contains subcommands) as parameter */}}
|
||||
|
@ -35,6 +34,9 @@ var (
|
|||
{{ $cmdPath := genZshFuncName .}}
|
||||
function {{$cmdPath}} {
|
||||
local -a commands
|
||||
{{/* If we are at the root, save a copy of the $words array as it contains the full command, including any empty
|
||||
strings and other parameters */}}
|
||||
{{ if (not .HasParent) and .HasDynamicCompletions }} full_command=("${(@)words}"){{- end}}
|
||||
|
||||
_arguments -C \{{- range extractFlags .}}
|
||||
{{genFlagEntryForZshArguments .}} \{{- end}}
|
||||
|
@ -82,7 +84,7 @@ function {{genZshFuncName .}} {
|
|||
{{define "Main" -}}
|
||||
#compdef _{{.Name}} {{.Name}}
|
||||
|
||||
{{if hasDynamicCompletions . -}}
|
||||
{{if .HasDynamicCompletions -}}
|
||||
function {{genZshFlagDynamicCompletionFuncName .}} {
|
||||
export COBRA_FLAG_COMPLETION="$1"
|
||||
|
||||
|
@ -91,7 +93,7 @@ function {{genZshFlagDynamicCompletionFuncName .}} {
|
|||
return $?
|
||||
fi
|
||||
|
||||
if ! error_message="$($tokens 2>&1 > "$output")" ; then
|
||||
if ! error_message="$("${(@)full_command}" 2>&1 > "$output")" ; then
|
||||
local st="$?"
|
||||
_message "Exception occurred during completion: $error_message"
|
||||
return "$st"
|
||||
|
@ -102,7 +104,11 @@ function {{genZshFlagDynamicCompletionFuncName .}} {
|
|||
args+="$line"
|
||||
done < "$output"
|
||||
|
||||
_values "$1" $args
|
||||
if [[ $#args -gt 0 ]] ; then
|
||||
_values "$1" "${(@)args}"
|
||||
else
|
||||
_message "No matching completion for $descr: $opt_args"
|
||||
fi
|
||||
|
||||
unset COBRA_FLAG_COMPLETION
|
||||
rm "$output"
|
||||
|
@ -141,12 +147,11 @@ func (c *Command) GenZshCompletionFile(filename string) error {
|
|||
// writer. The completion always run on the root command regardless of the
|
||||
// command it was called from.
|
||||
func (c *Command) GenZshCompletion(w io.Writer) error {
|
||||
visitAllFlagsWithCompletions(c, func(f *pflag.Flag) {
|
||||
if f.Annotations == nil {
|
||||
f.Annotations = make(map[string][]string)
|
||||
}
|
||||
f.Annotations[zshCompDynamicCompletion] = []string{zshCompGenDynamicFlagCompletionFuncName(c)}
|
||||
dynamicFlagCompletionFuncName := zshCompGenDynamicFlagCompletionFuncName(c.Root())
|
||||
c.Root().visitAllFlagsWithCompletions(func(f *pflag.Flag) {
|
||||
f.Annotations[zshCompDynamicCompletion] = []string{dynamicFlagCompletionFuncName + " " + f.Name}
|
||||
})
|
||||
|
||||
tmpl, err := template.New("Main").Funcs(zshCompFuncMap).Parse(zshCompletionText)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating zsh completion template: %v", err)
|
||||
|
@ -285,10 +290,6 @@ func zshCompGenFuncName(c *Command) string {
|
|||
return "_" + c.Name()
|
||||
}
|
||||
|
||||
func zshCompGenDynamicFlagCompletionFuncName(c *Command) string {
|
||||
return "_" + c.Root().Name() + "-flag-completion"
|
||||
}
|
||||
|
||||
func zshCompExtractFlag(c *Command) []*pflag.Flag {
|
||||
var flags []*pflag.Flag
|
||||
c.LocalFlags().VisitAll(func(f *pflag.Flag) {
|
||||
|
@ -360,7 +361,7 @@ func zshCompGenFlagEntryExtras(f *pflag.Flag) string {
|
|||
extras = extras + fmt.Sprintf(` -g "%s"`, pattern)
|
||||
}
|
||||
case zshCompDynamicCompletion:
|
||||
extras += fmt.Sprintf(":{%s %s}", values[0], f.Name)
|
||||
extras += fmt.Sprintf(":{%s}", values[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,14 +377,6 @@ func zshCompQuoteFlagDescription(s string) string {
|
|||
return strings.Replace(s, "'", `'\''`, -1)
|
||||
}
|
||||
|
||||
func zshCompHasDynamicCompletions(c *Command) bool {
|
||||
if len(c.flagCompletions) > 0 {
|
||||
return true
|
||||
}
|
||||
for _, subcommand := range c.Commands() {
|
||||
if zshCompHasDynamicCompletions(subcommand) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
func zshCompGenDynamicFlagCompletionFuncName(c *Command) string {
|
||||
return "_" + c.Root().Name() + "-handle-dynamic-flag-completion"
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue