forked from urfave/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds a new method `ToFishCompletion` to the `*App` which can be used to generate a fish completion string for the application. Relates to: urfave#351 Signed-off-by: Sascha Grunert <sgrunert@suse.com>
- Loading branch information
1 parent
946f918
commit 8bf6714
Showing
4 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
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 -c %s -f -n '%s' -a '%s'", | ||
a.Name, | ||
a.fishSubcommandHelper(previousCommands), | ||
strings.Join(command.Names(), " "), | ||
)) | ||
|
||
if command.Usage != "" { | ||
completion.WriteString(fmt.Sprintf(" -d '%s'", 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 | ||
} | ||
|
||
var completion strings.Builder | ||
completion.WriteString(fmt.Sprintf( | ||
"complete -c %s -f -n '%s'", | ||
a.Name, | ||
a.fishSubcommandHelper(previousCommands), | ||
)) | ||
|
||
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'", flag.GetUsage())) | ||
} | ||
|
||
completions = append(completions, completion.String()) | ||
} | ||
|
||
return completions | ||
} | ||
|
||
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 | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package cli | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestFishCompletion(t *testing.T) { | ||
// Given | ||
app := testApp() | ||
|
||
// When | ||
res, err := app.ToFishCompletion() | ||
|
||
// Then | ||
expect(t, err, nil) | ||
expectFileContent(t, "testdata/expected-fish-full.fish", res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# greet fish shell completion | ||
|
||
function __fish_greet_no_subcommand --description 'Test if there has been any subcommand yet' | ||
for i in (commandline -opc) | ||
if contains -- $i config c sub-config s ss info i in some-command | ||
return 1 | ||
end | ||
end | ||
return 0 | ||
end | ||
|
||
complete -c greet -f -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some usage text' | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -l flag -s fl -s f -r | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -l another-flag -s b -d 'another usage text' | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -l help -s h -d 'show help' | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -l version -s v -d 'print the version' | ||
complete -c greet -f -n '__fish_seen_subcommand_from config c' -l help -s h -d 'show help' | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -a 'config c' -d 'another usage test' | ||
complete -c greet -f -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f -r | ||
complete -c greet -f -n '__fish_seen_subcommand_from config c' -l another-flag -s b -d 'another usage text' | ||
complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l help -s h -d 'show help' | ||
complete -c greet -f -n '__fish_seen_subcommand_from config c' -a 'sub-config s ss' -d 'another usage test' | ||
complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l sub-flag -s sub-fl -s s -r | ||
complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l sub-command-flag -s s -d 'some usage text' | ||
complete -c greet -f -n '__fish_seen_subcommand_from info i in' -l help -s h -d 'show help' | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information' | ||
complete -c greet -f -n '__fish_seen_subcommand_from some-command' -l help -s h -d 'show help' | ||
complete -c greet -f -n '__fish_greet_no_subcommand' -a 'some-command' |