195 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			4.1 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]
 | 
						|
 | 
						|
		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)
 | 
						|
}
 |