diff options
Diffstat (limited to 'cmd/podman/common')
-rw-r--r-- | cmd/podman/common/completion.go | 76 | ||||
-rw-r--r-- | cmd/podman/common/completion_test.go | 13 |
2 files changed, 58 insertions, 31 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 9a4524b46..3966606e3 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -985,40 +985,14 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t f = f.Elem() } - // // the only supported type is struct + // the only supported type is struct if f.Kind() != reflect.Struct { return nil, cobra.ShellCompDirectiveNoFileComp } // last field get all names to suggest if i == len(fields)-1 { - suggestions := []string{} - for j := 0; j < f.NumField(); j++ { - fname := f.Type().Field(j).Name - suffix := "}}" - kind := f.Type().Field(j).Type.Kind() - if kind == reflect.Ptr { - // make sure to read the actual type when it is a pointer - kind = f.Type().Field(j).Type.Elem().Kind() - } - // when we have a nested struct do not append braces instead append a dot - if kind == reflect.Struct { - suffix = "." - } - if strings.HasPrefix(fname, fields[i]) { - // add field name with closing braces - suggestions = append(suggestions, fname+suffix) - } - } - - for j := 0; j < f.NumMethod(); j++ { - fname := f.Type().Method(j).Name - if strings.HasPrefix(fname, fields[i]) { - // add method name with closing braces - suggestions = append(suggestions, fname+"}}") - } - } - + suggestions := getStructFields(f, fields[i]) // add the current toComplete value in front so that the shell can complete this correctly toCompArr := strings.Split(toComplete, ".") toCompArr[len(toCompArr)-1] = "" @@ -1032,6 +1006,52 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t } } +// getStructFields reads all struct field names and method names and returns them. +func getStructFields(f reflect.Value, prefix string) []string { + suggestions := []string{} + // follow the pointer first + if f.Kind() == reflect.Ptr { + f = f.Elem() + } + // we only support structs + if f.Kind() != reflect.Struct { + return nil + } + // loop over all field names + for j := 0; j < f.NumField(); j++ { + field := f.Type().Field(j) + fname := field.Name + suffix := "}}" + kind := field.Type.Kind() + if kind == reflect.Ptr { + // make sure to read the actual type when it is a pointer + kind = field.Type.Elem().Kind() + } + // when we have a nested struct do not append braces instead append a dot + if kind == reflect.Struct { + suffix = "." + } + if strings.HasPrefix(fname, prefix) { + // add field name with suffix + suggestions = append(suggestions, fname+suffix) + } + // if field is anonymous add the child fields as well + if field.Anonymous { + suggestions = append(suggestions, getStructFields(f.FieldByIndex([]int{j}), prefix)...) + } + } + + for j := 0; j < f.NumMethod(); j++ { + fname := f.Type().Method(j).Name + if strings.HasPrefix(fname, prefix) { + // add method name with closing braces + suggestions = append(suggestions, fname+"}}") + } + } + + return suggestions +} + // AutocompleteEventFilter - Autocomplete event filter flag options. // -> "container=", "event=", "image=", "pod=", "volume=", "type=" func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go index 5bd627b85..84b3c1132 100644 --- a/cmd/podman/common/completion_test.go +++ b/cmd/podman/common/completion_test.go @@ -17,6 +17,10 @@ type Car struct { Extras map[string]string } +type Anonymous struct { + Hello string +} + func (c Car) Type() string { return "" } @@ -30,7 +34,10 @@ func TestAutocompleteFormat(t *testing.T) { Name string Age int Car *Car - }{} + *Anonymous + }{ + Anonymous: &Anonymous{}, + } testStruct.Car = &Car{} testStruct.Car.Extras = map[string]string{"test": "1"} @@ -73,12 +80,12 @@ func TestAutocompleteFormat(t *testing.T) { { "fist level struct field name", "{{.", - []string{"{{.Name}}", "{{.Age}}", "{{.Car."}, + []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Anonymous.", "{{.Hello}}"}, }, { "fist level struct field name", "{{ .", - []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car."}, + []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Anonymous.", "{{ .Hello}}"}, }, { "fist level struct field name", |