shell completion --format: work with pointer functions

The completion logic currently suggest also the functions that are
defined for this type. However this did not work correctly when it was
defined as pointer to that type on not the actual type.

This commit fixes that problem. To test you can compare the difference
between `podman stats --format {{.[TAB]` with and without this commit.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2022-05-12 13:08:03 +02:00
parent 28588235d2
commit 9df3906553
2 changed files with 24 additions and 15 deletions

View File

@ -992,13 +992,6 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
fields := strings.Split(field[len(field)-1], ".")
f := reflect.ValueOf(o)
for i := 1; i < len(fields); i++ {
val := getActualStructType(f)
if val == nil {
// no struct return nothing to complete
return nil, cobra.ShellCompDirectiveNoFileComp
}
f = *val
// last field get all names to suggest
if i == len(fields)-1 {
suggestions := getStructFields(f, fields[i])
@ -1008,6 +1001,14 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
toComplete = strings.Join(toCompArr, ".")
return prefixSlice(toComplete, suggestions), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
val := getActualStructType(f)
if val == nil {
// no struct return nothing to complete
return nil, cobra.ShellCompDirectiveNoFileComp
}
f = *val
// set the next struct field
f = f.FieldByName(fields[i])
}
@ -1038,12 +1039,15 @@ func getActualStructType(f reflect.Value) *reflect.Value {
// getStructFields reads all struct field names and method names and returns them.
func getStructFields(f reflect.Value, prefix string) []string {
suggestions := []string{}
var suggestions []string
if f.IsValid() {
suggestions = append(suggestions, getMethodNames(f, prefix)...)
}
val := getActualStructType(f)
if val == nil {
// no struct return nothing to complete
return nil
return suggestions
}
f = *val
@ -1069,7 +1073,11 @@ func getStructFields(f reflect.Value, prefix string) []string {
suggestions = append(suggestions, fname+suffix)
}
}
return suggestions
}
func getMethodNames(f reflect.Value, prefix string) []string {
suggestions := make([]string, 0, f.NumMethod())
for j := 0; j < f.NumMethod(); j++ {
fname := f.Type().Method(j).Name
if strings.HasPrefix(fname, prefix) {
@ -1077,7 +1085,6 @@ func getStructFields(f reflect.Value, prefix string) []string {
suggestions = append(suggestions, fname+"}}")
}
}
return suggestions
}

View File

@ -25,7 +25,9 @@ func (c Car) Type() string {
return ""
}
func (c Car) Color() string {
// Note: It is important that this function is *Car and the Type one is just Car.
// The reflect logic behaves differently for these cases so we have to test both.
func (c *Car) Color() string {
return ""
}
@ -94,7 +96,7 @@ func TestAutocompleteFormat(t *testing.T) {
{
"second level struct field name",
"{{ .Car.",
[]string{"{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras}}", "{{ .Car.Color}}", "{{ .Car.Type}}"},
[]string{"{{ .Car.Color}}", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras}}"},
},
{
"second level struct field name",
@ -104,7 +106,7 @@ func TestAutocompleteFormat(t *testing.T) {
{
"second level nil struct field name",
"{{ .Car2.",
[]string{"{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras}}", "{{ .Car2.Color}}", "{{ .Car2.Type}}"},
[]string{"{{ .Car2.Color}}", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras}}"},
},
{
"three level struct field name",
@ -134,8 +136,8 @@ func TestAutocompleteFormat(t *testing.T) {
{
"two variables struct field name",
"{{ .Car.Brand }} {{ .Car.",
[]string{"{{ .Car.Brand }} {{ .Car.Brand}}", "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras}}",
"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Type}}"},
[]string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Type}}", "{{ .Car.Brand }} {{ .Car.Brand}}",
"{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras}}"},
},
{
"only dot without variable",