Skip to content

Commit

Permalink
Better error message on unrecognized command
Browse files Browse the repository at this point in the history
Closes issue #1436

License: MIT
Signed-off-by: Shaun Bruce <[email protected]>
  • Loading branch information
sbruce committed Jul 15, 2015
1 parent 88ec46e commit fbdf39a
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 3 deletions.
4 changes: 4 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions commands/cli/cmd_suggestion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cli

import (
"sort"
"strings"

levenshtein "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/texttheater/golang-levenshtein/levenshtein"
cmds "github.com/ipfs/go-ipfs/commands"
)

// Make a custom slice that can be sorted by its levenshtein value
type suggestionSlice []*suggestion

type suggestion struct {
cmd string
levenshtein int
}

func (s suggestionSlice) Len() int {
return len(s)
}

func (s suggestionSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

func (s suggestionSlice) Less(i, j int) bool {
return s[i].levenshtein < s[j].levenshtein
}

func suggestUnknownCmd(args []string, root *cmds.Command) []string {
arg := args[0]
var suggestions []string
sortableSuggestions := make(suggestionSlice, 0)
var sFinal []string
const MIN_LEVENSHTEIN = 3

var options levenshtein.Options = levenshtein.Options{
InsCost: 1,
DelCost: 3,
SubCost: 2,
Matches: func(sourceCharacter rune, targetCharacter rune) bool {
return sourceCharacter == targetCharacter
},
}

// Start with a simple strings.Contains check
for name, _ := range root.Subcommands {
if strings.Contains(arg, name) {
suggestions = append(suggestions, name)
}
}

// If the string compare returns a match, return
if len(suggestions) > 0 {
return suggestions
}

for name, _ := range root.Subcommands {
lev := levenshtein.DistanceForStrings([]rune(arg), []rune(name), options)
if lev <= MIN_LEVENSHTEIN {
sortableSuggestions = append(sortableSuggestions, &suggestion{name, lev})
}
}
sort.Sort(sortableSuggestions)

for _, j := range sortableSuggestions {
sFinal = append(sFinal, j.cmd)
}
return sFinal
}
14 changes: 11 additions & 3 deletions commands/cli/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
}
}

stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive)
stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive, root)
if err != nil {
return req, cmd, path, err
}
Expand Down Expand Up @@ -196,7 +196,7 @@ func parseOpts(args []string, root *cmds.Command) (
return
}

func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []files.File, error) {
func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool, root *cmds.Command) ([]string, []files.File, error) {
// ignore stdin on Windows
if runtime.GOOS == "windows" {
stdin = nil
Expand Down Expand Up @@ -231,7 +231,15 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
// and the last arg definition is not variadic (or there are no definitions), return an error
notVariadic := len(argDefs) == 0 || !argDefs[len(argDefs)-1].Variadic
if notVariadic && len(inputs) > len(argDefs) {
return nil, nil, fmt.Errorf("Expected %v arguments, got %v: %v", len(argDefs), len(inputs), inputs)
suggestions := suggestUnknownCmd(inputs, root)

if len(suggestions) > 1 {
return nil, nil, fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean any of these?\n\n\t%s", inputs[0], strings.Join(suggestions, "\n\t"))
} else if len(suggestions) > 0 {
return nil, nil, fmt.Errorf("Unknown Command \"%s\"\n\nDid you mean this?\n\n\t%s", inputs[0], suggestions[0])
} else {
return nil, nil, fmt.Errorf("Unknown Command \"%s\"\n", inputs[0])
}
}

stringArgs := make([]string, 0, numInputs)
Expand Down

0 comments on commit fbdf39a

Please sign in to comment.