Merge pull request #1396 from urfave/suggestion-pluggability
Introduce override hooks for suggestions
This commit is contained in:
commit
9de0cd3c4a
33
app.go
33
app.go
@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const suggestDidYouMeanTemplate = "Did you mean %q?"
|
||||
|
||||
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,10 @@ 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
|
||||
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
|
||||
)
|
||||
|
||||
// App is the main structure of a cli application. It is recommended that
|
||||
@ -102,6 +108,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 {
|
||||
@ -343,6 +353,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(SuggestDidYouMeanTemplate+"\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
|
||||
|
@ -26,6 +26,11 @@ application:
|
||||
|
||||
VARIABLES
|
||||
|
||||
var (
|
||||
SuggestFlag SuggestFlagFunc = suggestFlag
|
||||
SuggestCommand SuggestCommandFunc = suggestCommand
|
||||
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
|
||||
)
|
||||
var AppHelpTemplate = `NAME:
|
||||
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
|
||||
|
||||
@ -1622,6 +1627,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.
|
||||
}
|
||||
|
2
help.go
2
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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
@ -71,5 +47,5 @@ func suggestCommand(commands []*Command, provided string) (suggestion string) {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(didYouMeanTemplate, suggestion)
|
||||
return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion)
|
||||
}
|
||||
|
@ -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")
|
||||
@ -57,7 +56,7 @@ func TestSuggestFlagFromError(t *testing.T) {
|
||||
)
|
||||
|
||||
// Then
|
||||
expect(t, res, fmt.Sprintf(didYouMeanTemplate+"\n\n", testCase.expected))
|
||||
expect(t, res, fmt.Sprintf(SuggestDidYouMeanTemplate+"\n\n", testCase.expected))
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +116,7 @@ func TestSuggestCommand(t *testing.T) {
|
||||
res := suggestCommand(app.Commands, testCase.provided)
|
||||
|
||||
// Then
|
||||
expect(t, res, fmt.Sprintf(didYouMeanTemplate, testCase.expected))
|
||||
expect(t, res, fmt.Sprintf(SuggestDidYouMeanTemplate, testCase.expected))
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +140,7 @@ func ExampleApp_Suggest() {
|
||||
// Output:
|
||||
// Incorrect Usage. flag provided but not defined: -nema
|
||||
//
|
||||
// Did you mean '--name'?
|
||||
// Did you mean "--name"?
|
||||
//
|
||||
// (this space intentionally left blank)
|
||||
}
|
||||
@ -182,7 +181,7 @@ func ExampleApp_Suggest_command() {
|
||||
// Output:
|
||||
// Incorrect Usage: flag provided but not defined: -sliming
|
||||
//
|
||||
// Did you mean '--smiling'?
|
||||
// Did you mean "--smiling"?
|
||||
//
|
||||
// (this space intentionally left blank)
|
||||
}
|
||||
|
9
testdata/godoc-v2.x.txt
vendored
9
testdata/godoc-v2.x.txt
vendored
@ -26,6 +26,11 @@ application:
|
||||
|
||||
VARIABLES
|
||||
|
||||
var (
|
||||
SuggestFlag SuggestFlagFunc = suggestFlag
|
||||
SuggestCommand SuggestCommandFunc = suggestCommand
|
||||
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
|
||||
)
|
||||
var AppHelpTemplate = `NAME:
|
||||
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
|
||||
|
||||
@ -1622,6 +1627,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.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user