
The switch to urfave/cli had a use of a /v2 API, which go modules handles correctly but vndr ignores. Downgrade urfave/cli for now until the switch to go modules. Add missing dependencies, which vndr now sees. Note that CI was not catching this issue, it seems that some part of the build process was pulling in dependencies even if they weren't in vendor, causing the build to work. However the vendor check was not seeing it. The ARM build didn't pull in other dependencies into the gopath, causing those builds to break. Signed-off-by: Derek McGowan <derek@mcgstyle.net>
191 lines
4.0 KiB
Go
191 lines
4.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"text/template"
|
|
)
|
|
|
|
// ToFishCompletion creates a fish completion string for the `*App`
|
|
// The function errors if either parsing or writing of the string fails.
|
|
func (a *App) ToFishCompletion() (string, error) {
|
|
var w bytes.Buffer
|
|
if err := a.writeFishCompletionTemplate(&w); err != nil {
|
|
return "", err
|
|
}
|
|
return w.String(), nil
|
|
}
|
|
|
|
type fishCompletionTemplate struct {
|
|
App *App
|
|
Completions []string
|
|
AllCommands []string
|
|
}
|
|
|
|
func (a *App) writeFishCompletionTemplate(w io.Writer) error {
|
|
const name = "cli"
|
|
t, err := template.New(name).Parse(FishCompletionTemplate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
allCommands := []string{}
|
|
|
|
// Add global flags
|
|
completions := a.prepareFishFlags(a.VisibleFlags(), allCommands)
|
|
|
|
// Add help flag
|
|
if !a.HideHelp {
|
|
completions = append(
|
|
completions,
|
|
a.prepareFishFlags([]Flag{HelpFlag}, allCommands)...,
|
|
)
|
|
}
|
|
|
|
// Add version flag
|
|
if !a.HideVersion {
|
|
completions = append(
|
|
completions,
|
|
a.prepareFishFlags([]Flag{VersionFlag}, allCommands)...,
|
|
)
|
|
}
|
|
|
|
// Add commands and their flags
|
|
completions = append(
|
|
completions,
|
|
a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})...,
|
|
)
|
|
|
|
return t.ExecuteTemplate(w, name, &fishCompletionTemplate{
|
|
App: a,
|
|
Completions: completions,
|
|
AllCommands: allCommands,
|
|
})
|
|
}
|
|
|
|
func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, previousCommands []string) []string {
|
|
completions := []string{}
|
|
for i := range commands {
|
|
command := &commands[i]
|
|
|
|
var completion strings.Builder
|
|
completion.WriteString(fmt.Sprintf(
|
|
"complete -r -c %s -n '%s' -a '%s'",
|
|
a.Name,
|
|
a.fishSubcommandHelper(previousCommands),
|
|
strings.Join(command.Names(), " "),
|
|
))
|
|
|
|
if command.Usage != "" {
|
|
completion.WriteString(fmt.Sprintf(" -d '%s'",
|
|
escapeSingleQuotes(command.Usage)))
|
|
}
|
|
|
|
if !command.HideHelp {
|
|
completions = append(
|
|
completions,
|
|
a.prepareFishFlags([]Flag{HelpFlag}, command.Names())...,
|
|
)
|
|
}
|
|
|
|
*allCommands = append(*allCommands, command.Names()...)
|
|
completions = append(completions, completion.String())
|
|
completions = append(
|
|
completions,
|
|
a.prepareFishFlags(command.Flags, command.Names())...,
|
|
)
|
|
|
|
// recursevly iterate subcommands
|
|
if len(command.Subcommands) > 0 {
|
|
completions = append(
|
|
completions,
|
|
a.prepareFishCommands(
|
|
command.Subcommands, allCommands, command.Names(),
|
|
)...,
|
|
)
|
|
}
|
|
}
|
|
|
|
return completions
|
|
}
|
|
|
|
func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
|
|
completions := []string{}
|
|
for _, f := range flags {
|
|
flag, ok := f.(DocGenerationFlag)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
completion := &strings.Builder{}
|
|
completion.WriteString(fmt.Sprintf(
|
|
"complete -c %s -n '%s'",
|
|
a.Name,
|
|
a.fishSubcommandHelper(previousCommands),
|
|
))
|
|
|
|
fishAddFileFlag(f, completion)
|
|
|
|
for idx, opt := range strings.Split(flag.GetName(), ",") {
|
|
if idx == 0 {
|
|
completion.WriteString(fmt.Sprintf(
|
|
" -l %s", strings.TrimSpace(opt),
|
|
))
|
|
} else {
|
|
completion.WriteString(fmt.Sprintf(
|
|
" -s %s", strings.TrimSpace(opt),
|
|
))
|
|
|
|
}
|
|
}
|
|
|
|
if flag.TakesValue() {
|
|
completion.WriteString(" -r")
|
|
}
|
|
|
|
if flag.GetUsage() != "" {
|
|
completion.WriteString(fmt.Sprintf(" -d '%s'",
|
|
escapeSingleQuotes(flag.GetUsage())))
|
|
}
|
|
|
|
completions = append(completions, completion.String())
|
|
}
|
|
|
|
return completions
|
|
}
|
|
|
|
func fishAddFileFlag(flag Flag, completion *strings.Builder) {
|
|
switch f := flag.(type) {
|
|
case GenericFlag:
|
|
if f.TakesFile {
|
|
return
|
|
}
|
|
case StringFlag:
|
|
if f.TakesFile {
|
|
return
|
|
}
|
|
case StringSliceFlag:
|
|
if f.TakesFile {
|
|
return
|
|
}
|
|
}
|
|
completion.WriteString(" -f")
|
|
}
|
|
|
|
func (a *App) fishSubcommandHelper(allCommands []string) string {
|
|
fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name)
|
|
if len(allCommands) > 0 {
|
|
fishHelper = fmt.Sprintf(
|
|
"__fish_seen_subcommand_from %s",
|
|
strings.Join(allCommands, " "),
|
|
)
|
|
}
|
|
return fishHelper
|
|
|
|
}
|
|
|
|
func escapeSingleQuotes(input string) string {
|
|
return strings.Replace(input, `'`, `\'`, -1)
|
|
}
|