|
- 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]
-
- if command.Hidden {
- continue
- }
-
- 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)
- }
|