From 68bd4903fd87f937c504c6dc44d3920cb0ecf11f Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 14 May 2022 08:24:44 -0400 Subject: [PATCH] Introduce override hooks for suggestions Related to https://github.com/urfave/cli/pull/1390#discussion_r871398659 --- app.go | 32 ++++++++++++++++++++++++++++++++ godoc-current.txt | 8 ++++++++ help.go | 2 +- suggestions.go | 30 +++--------------------------- suggestions_test.go | 5 ++--- testdata/godoc-v2.x.txt | 8 ++++++++ 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/app.go b/app.go index 463437a58a..45c5ab2cd2 100644 --- a/app.go +++ b/app.go @@ -11,6 +11,8 @@ import ( "time" ) +const didYouMeanTemplate = "Did you mean '%s'?" + var ( changeLogURL = "https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md" appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) @@ -18,6 +20,9 @@ var ( errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) + + SuggestFlag SuggestFlagFunc = suggestFlag + SuggestCommand SuggestCommandFunc = suggestCommand ) // App is the main structure of a cli application. It is recommended that @@ -100,6 +105,10 @@ type App struct { didSetup bool } +type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string + +type SuggestCommandFunc func(commands []*Command, provided string) string + // Tries to find out when this binary was compiled. // Returns the current time if it fails to find it. func compileTime() time.Time { @@ -332,6 +341,29 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { return err } +func (a *App) suggestFlagFromError(err error, command string) (string, error) { + flag, parseErr := flagFromError(err) + if parseErr != nil { + return "", err + } + + flags := a.Flags + if command != "" { + cmd := a.Command(command) + if cmd == nil { + return "", err + } + flags = cmd.Flags + } + + suggestion := SuggestFlag(flags, flag, a.HideHelp) + if len(suggestion) == 0 { + return "", err + } + + return fmt.Sprintf(didYouMeanTemplate+"\n\n", suggestion), nil +} + // RunAndExitOnError calls .Run() and exits non-zero if an error was returned // // Deprecated: instead you should return an error that fulfills cli.ExitCoder diff --git a/godoc-current.txt b/godoc-current.txt index 90700ebde7..e1879ee971 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -26,6 +26,10 @@ application: VARIABLES +var ( + SuggestFlag SuggestFlagFunc = suggestFlag + SuggestCommand SuggestCommandFunc = suggestCommand +) var AppHelpTemplate = `NAME: {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} @@ -1539,6 +1543,10 @@ func (f *StringSliceFlag) String() string func (f *StringSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +type SuggestCommandFunc func(commands []*Command, provided string) string + +type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string + type Timestamp struct { // Has unexported fields. } diff --git a/help.go b/help.go index 51033928cb..1ab7a6a9db 100644 --- a/help.go +++ b/help.go @@ -221,7 +221,7 @@ func ShowCommandHelp(ctx *Context, command string) error { if ctx.App.CommandNotFound == nil { errMsg := fmt.Sprintf("No help topic for '%v'", command) if ctx.App.Suggest { - if suggestion := suggestCommand(ctx.App.Commands, command); suggestion != "" { + if suggestion := SuggestCommand(ctx.App.Commands, command); suggestion != "" { errMsg += ". " + suggestion } } diff --git a/suggestions.go b/suggestions.go index 476af4de50..8df0b32a26 100644 --- a/suggestions.go +++ b/suggestions.go @@ -6,37 +6,13 @@ import ( "github.com/antzucaro/matchr" ) -const didYouMeanTemplate = "Did you mean '%s'?" - -func (a *App) suggestFlagFromError(err error, command string) (string, error) { - flag, parseErr := flagFromError(err) - if parseErr != nil { - return "", err - } - - flags := a.Flags - if command != "" { - cmd := a.Command(command) - if cmd == nil { - return "", err - } - flags = cmd.Flags - } - - suggestion := a.suggestFlag(flags, flag) - if len(suggestion) == 0 { - return "", err - } - - return fmt.Sprintf(didYouMeanTemplate+"\n\n", suggestion), nil -} - -func (a *App) suggestFlag(flags []Flag, provided string) (suggestion string) { +func suggestFlag(flags []Flag, provided string, hideHelp bool) string { distance := 0.0 + suggestion := "" for _, flag := range flags { flagNames := flag.Names() - if !a.HideHelp { + if !hideHelp { flagNames = append(flagNames, HelpFlag.Names()...) } for _, name := range flagNames { diff --git a/suggestions_test.go b/suggestions_test.go index 4ebe9c0c7f..c2a3d17ec9 100644 --- a/suggestions_test.go +++ b/suggestions_test.go @@ -20,7 +20,7 @@ func TestSuggestFlag(t *testing.T) { {"s", "-s"}, } { // When - res := app.suggestFlag(app.Flags, testCase.provided) + res := suggestFlag(app.Flags, testCase.provided, false) // Then expect(t, res, testCase.expected) @@ -30,10 +30,9 @@ func TestSuggestFlag(t *testing.T) { func TestSuggestFlagHideHelp(t *testing.T) { // Given app := testApp() - app.HideHelp = true // When - res := app.suggestFlag(app.Flags, "hlp") + res := suggestFlag(app.Flags, "hlp", true) // Then expect(t, res, "--fl") diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 90700ebde7..e1879ee971 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -26,6 +26,10 @@ application: VARIABLES +var ( + SuggestFlag SuggestFlagFunc = suggestFlag + SuggestCommand SuggestCommandFunc = suggestCommand +) var AppHelpTemplate = `NAME: {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} @@ -1539,6 +1543,10 @@ func (f *StringSliceFlag) String() string func (f *StringSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +type SuggestCommandFunc func(commands []*Command, provided string) string + +type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string + type Timestamp struct { // Has unexported fields. }