fix autocompletion for api args and filterargs
Signed-off-by: Rohit Yadav <rohit@apache.org>
diff --git a/cli/completer.go b/cli/completer.go
index d2fbcd6..8a161fa 100644
--- a/cli/completer.go
+++ b/cli/completer.go
@@ -99,16 +99,65 @@
return false
}
-type autoCompleter struct {
- Config *config.Config
+func lastString(array []string) string {
+ return array[len(array)-1]
}
-type selectOption struct {
- ID string
- Name string
+type argOption struct {
+ Value string
Detail string
}
+func buildArgOptions(response map[string]interface{}, hasID bool) []argOption {
+ argOptions := []argOption{}
+ for _, v := range response {
+ switch obj := v.(type) {
+ case []interface{}:
+ if obj == nil {
+ break
+ }
+ for _, item := range obj {
+ resource, ok := item.(map[string]interface{})
+ if !ok {
+ continue
+ }
+ var id, name, detail string
+ if resource["id"] != nil {
+ id = resource["id"].(string)
+ }
+ if resource["name"] != nil {
+ name = resource["name"].(string)
+ } else if resource["username"] != nil {
+ name = resource["username"].(string)
+ }
+ if resource["displaytext"] != nil {
+ detail = resource["displaytext"].(string)
+ }
+ if len(detail) == 0 && resource["description"] != nil {
+ detail = resource["description"].(string)
+ }
+ if len(detail) == 0 && resource["ipaddress"] != nil {
+ detail = resource["ipaddress"].(string)
+ }
+ var opt argOption
+ if hasID {
+ opt.Value = id
+ opt.Detail = name
+ if len(name) == 0 {
+ opt.Detail = detail
+ }
+ } else {
+ opt.Value = name
+ opt.Detail = detail
+ }
+ argOptions = append(argOptions, opt)
+ }
+ break
+ }
+ }
+ return argOptions
+}
+
func doInternal(line []rune, pos int, lineLen int, argName []rune) (newLine [][]rune, offset int) {
offset = lineLen
if lineLen >= len(argName) {
@@ -128,6 +177,10 @@
return
}
+type autoCompleter struct {
+ Config *config.Config
+}
+
func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int) {
apiMap := buildAPICacheMap(t.Config.GetAPIVerbMap())
@@ -189,7 +242,7 @@
return
}
- // Auto-complete api args
+ // Auto-complete API arg
splitLine := strings.Split(string(line), " ")
line = trimSpaceLeft([]rune(splitLine[len(splitLine)-1]))
for _, arg := range apiFound.Args {
@@ -199,16 +252,30 @@
options = append(options, sLine...)
offset = sOffset
} else {
+ words := strings.Split(string(line), "=")
+ argInput := lastString(words)
if arg.Type == "boolean" {
- options = [][]rune{[]rune("true "), []rune("false ")}
- offset = 0
+ for _, search := range []string{"true ", "false "} {
+ offset = 0
+ if strings.HasPrefix(search, argInput) {
+ options = append(options, []rune(search[len(argInput):]))
+ offset = len(argInput)
+ }
+ }
return
}
if arg.Type == config.FAKE && arg.Name == "filter=" {
- options = [][]rune{}
offset = 0
+ filterInputs := strings.Split(strings.Replace(argInput, ",", ",|", -1), "|")
+ lastFilterInput := lastString(filterInputs)
for _, key := range apiFound.ResponseKeys {
- options = append(options, []rune(key))
+ if inArray(key, filterInputs) {
+ continue
+ }
+ if strings.HasPrefix(key, lastFilterInput) {
+ options = append(options, []rune(key[len(lastFilterInput):]))
+ offset = len(lastFilterInput)
+ }
}
return
}
@@ -260,87 +327,41 @@
return nil, 0
}
- r := cmd.NewRequest(nil, completer.Config, nil)
autocompleteAPIArgs := []string{"listall=true"}
if autocompleteAPI.Noun == "templates" {
autocompleteAPIArgs = append(autocompleteAPIArgs, "templatefilter=executable")
}
spinner := t.Config.StartSpinner("fetching options, please wait...")
- response, _ := cmd.NewAPIRequest(r, autocompleteAPI.Name, autocompleteAPIArgs, false)
+ request := cmd.NewRequest(nil, completer.Config, nil)
+ response, _ := cmd.NewAPIRequest(request, autocompleteAPI.Name, autocompleteAPIArgs, false)
t.Config.StopSpinner(spinner)
- selectOptions := []selectOption{}
- for _, v := range response {
- switch obj := v.(type) {
- case []interface{}:
- if obj == nil {
- break
- }
- for _, item := range obj {
- resource, ok := item.(map[string]interface{})
- if !ok {
- continue
- }
- var opt selectOption
- if resource["id"] != nil {
- opt.ID = resource["id"].(string)
- }
- if resource["name"] != nil {
- opt.Name = resource["name"].(string)
- } else if resource["username"] != nil {
- opt.Name = resource["username"].(string)
- }
- if resource["displaytext"] != nil {
- opt.Detail = resource["displaytext"].(string)
- }
- if len(opt.Detail) == 0 && resource["description"] != nil {
- opt.Detail = resource["description"].(string)
- }
- if len(opt.Detail) == 0 && resource["ipaddress"] != nil {
- opt.Detail = resource["ipaddress"].(string)
- }
- selectOptions = append(selectOptions, opt)
- }
- break
- }
- }
-
- sort.Slice(selectOptions, func(i, j int) bool {
- return selectOptions[i].Name < selectOptions[j].Name
- })
-
hasID := strings.HasSuffix(arg.Name, "id=") || strings.HasSuffix(arg.Name, "ids=")
- if len(selectOptions) > 1 {
- for _, item := range selectOptions {
- var option string
- if hasID {
- if len(item.Name) > 0 {
- option = fmt.Sprintf("%v (%v)", item.ID, item.Name)
- } else {
- option = fmt.Sprintf("%v (%v)", item.ID, item.Detail)
- }
- } else {
- if len(item.Detail) == 0 {
- option = fmt.Sprintf("%v ", item.Name)
- } else {
- option = fmt.Sprintf("%v (%v)", item.Name, item.Detail)
- }
- }
- options = append(options, []rune(option))
- }
- } else {
- option := ""
- if len(selectOptions) == 1 {
- if hasID {
- option = selectOptions[0].ID
- } else {
- option = selectOptions[0].Name
+ argOptions := buildArgOptions(response, hasID)
+
+ filteredOptions := []argOption{}
+ if len(argOptions) > 0 {
+ sort.Slice(argOptions, func(i, j int) bool {
+ return argOptions[i].Value < argOptions[j].Value
+ })
+ for _, item := range argOptions {
+ if strings.HasPrefix(item.Value, argInput) {
+ filteredOptions = append(filteredOptions, item)
}
}
- options = [][]rune{[]rune(option + " ")}
}
offset = 0
+ for _, item := range filteredOptions {
+ option := item.Value + " "
+ if len(filteredOptions) > 1 && len(item.Detail) > 0 {
+ option += fmt.Sprintf("(%v)", item.Detail)
+ }
+ if strings.HasPrefix(option, argInput) {
+ options = append(options, []rune(option[len(argInput):]))
+ offset = len(argInput)
+ }
+ }
return
}
}
diff --git a/config/cache.go b/config/cache.go
index 19bc1cd..4320c61 100644
--- a/config/cache.go
+++ b/config/cache.go
@@ -146,7 +146,7 @@
// Add filter arg
apiArgs = append(apiArgs, &APIArg{
- Name: "filter",
+ Name: "filter=",
Type: FAKE,
Description: "cloudmonkey specific response key filtering",
})
@@ -164,6 +164,7 @@
responseKeys = append(responseKeys, fmt.Sprintf("%v,", resp["name"]))
}
}
+ sort.Strings(responseKeys)
var requiredArgs []string
for _, arg := range apiArgs {