Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.1 KiB
194 lines
4.1 KiB
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) |
|
}
|
|
|