in cli/completer.go [277:435]
func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int) {
apiMap := buildAPICacheMap(t.Config.GetAPIVerbMap())
var verbs []string
for verb := range apiMap {
verbs = append(verbs, verb)
sort.Slice(apiMap[verb], func(i, j int) bool {
return apiMap[verb][i].Name < apiMap[verb][j].Name
})
}
sort.Strings(verbs)
line = trimSpaceLeft(line[:pos])
// Auto-complete verb
var verbFound string
for _, verb := range verbs {
search := verb + " "
if !hasPrefix(line, []rune(search)) {
sLine, sOffset := doInternal(line, pos, len(line), []rune(search))
options = append(options, sLine...)
offset = sOffset
} else {
verbFound = verb
break
}
}
if len(verbFound) == 0 {
return
}
// Auto-complete noun
var nounFound string
line = trimSpaceLeft(line[len(verbFound):])
for _, api := range apiMap[verbFound] {
search := api.Noun + " "
if !hasPrefix(line, []rune(search)) {
sLine, sOffset := doInternal(line, pos, len(line), []rune(search))
options = append(options, sLine...)
offset = sOffset
} else {
nounFound = api.Noun
break
}
}
if len(nounFound) == 0 {
return
}
// Find API
var apiFound *config.API
for _, api := range apiMap[verbFound] {
if api.Noun == nounFound {
apiFound = api
break
}
}
if apiFound == nil {
return
}
// Auto-complete API arg
splitLine := strings.Split(string(line), " ")
line = trimSpaceLeft([]rune(splitLine[len(splitLine)-1]))
for _, arg := range apiFound.Args {
search := arg.Name
if !hasPrefix(line, []rune(search)) {
sLine, sOffset := doInternal(line, pos, len(line), []rune(search))
options = append(options, sLine...)
offset = sOffset
} else {
words := strings.Split(string(line), "=")
argInput := lastString(words)
if arg.Type == "boolean" {
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=" {
offset = 0
filterInputs := strings.Split(strings.Replace(argInput, ",", ",|", -1), "|")
lastFilterInput := lastString(filterInputs)
for _, key := range apiFound.ResponseKeys {
if inArray(key, filterInputs) {
continue
}
if strings.HasPrefix(key, lastFilterInput) {
options = append(options, []rune(key[len(lastFilterInput):]))
offset = len(lastFilterInput)
}
}
return
}
autocompleteAPI := findAutocompleteAPI(arg, apiFound, apiMap)
if autocompleteAPI == nil {
return nil, 0
}
completeArgs := t.Config.Core.AutoComplete
autocompleteAPIArgs := []string{}
argOptions := []argOption{}
if completeArgs {
autocompleteAPIArgs = []string{"listall=true"}
if autocompleteAPI.Noun == "templates" {
autocompleteAPIArgs = append(autocompleteAPIArgs, "templatefilter=executable")
}
if apiFound.Name != "provisionCertificate" && autocompleteAPI.Name == "listHosts" {
autocompleteAPIArgs = append(autocompleteAPIArgs, "type=Routing")
} else if apiFound.Name == "migrateSystemVm" {
autocompleteAPI.Name = "listSystemVms"
}
spinner := t.Config.StartSpinner("fetching options, please wait...")
request := cmd.NewRequest(nil, completer.Config, nil)
response, _ := cmd.NewAPIRequest(request, autocompleteAPI.Name, autocompleteAPIArgs, false)
t.Config.StopSpinner(spinner)
hasID := strings.HasSuffix(arg.Name, "id=") || strings.HasSuffix(arg.Name, "ids=")
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)
}
}
}
offset = 0
if len(filteredOptions) == 0 {
options = [][]rune{[]rune("")}
}
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
}
}
return options, offset
}