forked from shipwright-io/cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Command Suggestion for Incorrect Subcommands
Earlier there were no suggestions for shp subcommands but with this patch entering wrong subcommands will give suggestions. example:- shp build cr Error: unknown command "cr" for "shp build" Did you mean this? create Signed-off-by: vinamra28 <vinjain@redhat.com>
- Loading branch information
Showing
11 changed files
with
469 additions
and
1 deletion.
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
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
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,90 @@ | ||
package suggestion | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/texttheater/golang-levenshtein/levenshtein" | ||
) | ||
|
||
// SubcommandsRequiredWithSuggestions will ensure we have a subcommand provided by the user and augments it with | ||
// suggestion for commands, alias and help on root command. | ||
func SubcommandsRequiredWithSuggestions(cmd *cobra.Command, args []string) error { | ||
requireMsg := "unknown command %q for %q" | ||
typedName := "" | ||
// This will be triggered if cobra didn't find any subcommands. | ||
// Find some suggestions. | ||
var suggestions []string | ||
|
||
if len(args) != 0 && !cmd.DisableSuggestions { | ||
typedName += args[0] | ||
if cmd.SuggestionsMinimumDistance <= 0 { | ||
cmd.SuggestionsMinimumDistance = 2 | ||
} | ||
// subcommand suggestions | ||
suggestions = cmd.SuggestionsFor(args[0]) | ||
|
||
// subcommand alias suggestions (with distance, not exact) | ||
for _, c := range cmd.Commands() { | ||
if !c.IsAvailableCommand() { | ||
continue | ||
} | ||
|
||
candidate := suggestsByPrefixOrLd(typedName, c.Name(), cmd.SuggestionsMinimumDistance) | ||
if candidate == "" { | ||
continue | ||
} | ||
_, found := Find(suggestions, candidate) | ||
if !found { | ||
suggestions = append(suggestions, candidate) | ||
} | ||
} | ||
|
||
// help for root command | ||
if !cmd.HasParent() { | ||
candidate := suggestsByPrefixOrLd(typedName, "help", cmd.SuggestionsMinimumDistance) | ||
if candidate != "" { | ||
suggestions = append(suggestions, candidate) | ||
} | ||
} | ||
} | ||
|
||
var suggestionsMsg string | ||
if len(suggestions) > 0 { | ||
suggestionsMsg += "\nDid you mean this?\n" | ||
for _, s := range suggestions { | ||
suggestionsMsg += fmt.Sprintf("\t%v\n", s) | ||
} | ||
} | ||
|
||
if suggestionsMsg != "" { | ||
requireMsg = fmt.Sprintf("%s\n%s", requireMsg, suggestionsMsg) | ||
return fmt.Errorf(requireMsg, typedName, cmd.CommandPath()) | ||
} | ||
|
||
return cmd.Help() | ||
} | ||
|
||
// suggestsByPrefixOrLd suggests a command by levenshtein distance or by prefix. | ||
// It returns an empty string if nothing was found | ||
func suggestsByPrefixOrLd(typedName, candidate string, minDistance int) string { | ||
levenshteinVariable := levenshtein.DistanceForStrings([]rune(typedName), []rune(candidate), levenshtein.DefaultOptions) | ||
suggestByLevenshtein := levenshteinVariable <= minDistance | ||
suggestByPrefix := strings.HasPrefix(strings.ToLower(candidate), strings.ToLower(typedName)) | ||
if !suggestByLevenshtein && !suggestByPrefix { | ||
return "" | ||
} | ||
return candidate | ||
} | ||
|
||
// Find takes a slice and looks for an element in it. If found it will | ||
// return it's key, otherwise it will return -1 and a bool of false. | ||
func Find(slice []string, val string) (int, bool) { | ||
for i, item := range slice { | ||
if item == val { | ||
return i, true | ||
} | ||
} | ||
return -1, false | ||
} |
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,25 @@ | ||
package suggestion | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"testing" | ||
|
||
"github.com/onsi/gomega" | ||
"github.com/shipwright-io/cli/pkg/shp/cmd/build" | ||
|
||
"k8s.io/cli-runtime/pkg/genericclioptions" | ||
) | ||
|
||
func TestSuggestion(t *testing.T) { | ||
g := gomega.NewGomegaWithT(t) | ||
|
||
genericOpts := &genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} | ||
cmd := build.Command(nil, genericOpts) | ||
|
||
err := SubcommandsRequiredWithSuggestions(cmd, []string{"cr"}) | ||
|
||
expected := fmt.Sprintf("unknown command %q for %q\n\nDid you mean this?\n\t%s\n", "cr", "build", "create") | ||
|
||
g.Expect(err.Error()).To(gomega.Equal(expected)) | ||
} |
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,27 @@ | ||
package stub | ||
|
||
import ( | ||
"bytes" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
// ExecuteCommand executes the root command passing the args and returns | ||
// the output as a string and error | ||
func ExecuteCommand(root *cobra.Command, args ...string) (string, error) { | ||
_, output, err := ExecuteCommandC(root, args...) | ||
return output, err | ||
} | ||
|
||
// ExecuteCommandC executes the root command passing the args and returns | ||
// the root command, output as a string and error if any | ||
func ExecuteCommandC(c *cobra.Command, args ...string) (*cobra.Command, string, error) { | ||
buf := new(bytes.Buffer) | ||
c.SetOutput(buf) | ||
c.SetArgs(args) | ||
c.SilenceUsage = true | ||
|
||
root, err := c.ExecuteC() | ||
|
||
return root, buf.String(), err | ||
} |
21 changes: 21 additions & 0 deletions
21
vendor/github.com/texttheater/golang-levenshtein/levenshtein/LICENSE
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
3 changes: 3 additions & 0 deletions
3
vendor/github.com/texttheater/golang-levenshtein/levenshtein/go.mod
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.