Added the project auto completion code (#959)

* Added the project auto completion code

Signed-off-by: mik-dass <mrinald7@gmail.com>

* Added getUserTypedCommands function fro getting user typed entitites

Signed-off-by: mik-dass <mrinald7@gmail.com>
This commit is contained in:
Mrinal Das
2018-11-15 15:04:16 +05:30
committed by Chris Laprun
parent 6ddd306701
commit 80e299f8ea
5 changed files with 85 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/spf13/cobra"
"os"
"strings"
@@ -151,6 +152,7 @@ func printUnmountedStorage(client *occlient.Client, applicationName string) {
func addProjectFlag(cmd *cobra.Command) {
cmd.Flags().String(util.ProjectFlagName, "", "Project, defaults to active project")
completion.RegisterCommandFlagHandler(cmd, "project", completion.ProjectNameCompletionHandler)
}
func addComponentFlag(cmd *cobra.Command) {

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"os"
"strings"
@@ -200,6 +201,7 @@ func init() {
projectGetCmd.Flags().BoolVarP(&projectShortFlag, "short", "q", false, "If true, display only the project name")
projectSetCmd.Flags().BoolVarP(&projectShortFlag, "short", "q", false, "If true, display only the project name")
projectDeleteCmd.Flags().BoolVarP(&projectForceDeleteFlag, "force", "f", false, "Delete project without prompting")
projectDeleteCmd.Flags().BoolVarP(&projectShortFlag, "short", "q", false, "Delete project without prompting")
projectCmd.Flags().AddFlagSet(projectGetCmd.Flags())
projectCmd.AddCommand(projectGetCmd)
@@ -212,5 +214,8 @@ func init() {
projectCmd.Annotations = map[string]string{"command": "other"}
projectCmd.SetUsageTemplate(cmdUsageTemplate)
completion.RegisterCommandHandler(projectSetCmd, completion.ProjectNameCompletionHandler)
completion.RegisterCommandHandler(projectDeleteCmd, completion.ProjectNameCompletionHandler)
rootCmd.AddCommand(projectCmd)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/redhat-developer/odo/pkg/occlient"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ktesting "k8s.io/client-go/testing"
@@ -20,17 +21,20 @@ func TestCompletions(t *testing.T) {
tests := []struct {
name string
handler completion.ContextualizedPredictor
cmd *cobra.Command
last string
want []string
}{
{
name: "Completing service create without input returns all available service class external names",
handler: completion.ServiceClassCompletionHandler,
cmd: serviceCreateCmd,
want: []string{"foo", "bar", "boo"},
},
{
name: "Completing service delete without input returns all available service instances",
handler: completion.ServiceCompletionHandler,
cmd: serviceDeleteCmd,
want: []string{"foo"},
},
}
@@ -67,7 +71,9 @@ func TestCompletions(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := complete.Args{Last: tt.last}
got := tt.handler(nil, a, context)
got := tt.handler(tt.cmd, a, context)
if !equal(got, tt.want) {
t.Errorf("Failed %s: got: %q, want: %q", t.Name(), got, tt.want)
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/posener/complete"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
type completionHandler struct {
@@ -77,3 +78,52 @@ func GetCommandFlagHandler(command *cobra.Command, flag string) (predictor compl
predictor, ok = completionHandlers[getCommandFlagCompletionHandlerKey(command, flag)]
return
}
// getCommandsAndFlags returns the commands and flags from the given input
func getCommandsAndFlags(args []string, c *cobra.Command) (map[string]bool, map[string]string) {
strippedCommandsMap := make(map[string]bool)
setFlags := make(map[string]string)
if len(args) == 0 {
return strippedCommandsMap, setFlags
}
err := c.ParseFlags(args)
if err != nil {
return strippedCommandsMap, setFlags
}
flags := c.Flags()
cmds := flags.Args()
flags.Visit(func(i *flag.Flag) {
if i.Value.Type() != "bool" {
setFlags[i.Name] = i.Value.String()
}
})
// send a map of commands for faster searching
for _, strippedCommand := range cmds {
strippedCommandsMap[strippedCommand] = true
}
return strippedCommandsMap, setFlags
}
// getUserTypedCommands returns only the user typed entities by excluding the cobra predefined commands
func getUserTypedCommands(args complete.Args, command *cobra.Command) []string {
var commands []string
// get only the user typed commands/flags and remove the cobra defined commands
found := false
for _, arg := range args.Completed {
if arg == command.Name() && !found {
found = true
continue
}
if found {
commands = append(commands, arg)
}
}
return commands
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/posener/complete"
"github.com/redhat-developer/odo/pkg/application"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/project"
"github.com/redhat-developer/odo/pkg/service"
"github.com/spf13/cobra"
)
@@ -59,3 +60,23 @@ var FileCompletionHandler = func(cmd *cobra.Command, args complete.Args, context
completions = append(completions, complete.PredictFiles("*").Predict(args)...)
return
}
// ProjectNameCompletionHandler provides project name completion
var ProjectNameCompletionHandler = func(cmd *cobra.Command, args complete.Args, context *genericclioptions.Context) (completions []string) {
completions = make([]string, 0)
projects, err := project.List(context.Client)
if err != nil {
return completions
}
// extract the flags and commands from the user typed commands
strippedCommands, _ := getCommandsAndFlags(getUserTypedCommands(args, cmd), cmd)
for _, project := range projects {
// we found the project name in the list which means
// that the project name has been already selected by the user so no need to suggest more
if val, ok := strippedCommands[project.Name]; ok && val {
return nil
}
completions = append(completions, project.Name)
}
return completions
}