From 61ac9574eb90e707532b74a7912780b65e89f248 Mon Sep 17 00:00:00 2001 From: Mark Benjamin Date: Thu, 5 Dec 2024 14:45:24 -0500 Subject: [PATCH] Add enums to flags (#165) * Moving form keymap settings * Add enum flag * Parse enum * Add enum to form * Add in multi-select * Add more multi select * Add enum tests * Make case insensitive * Use EqualFold Co-authored-by: Max Eshleman --------- Co-authored-by: Max Eshleman --- cmd/logs.go | 17 ++++-- pkg/command/enum.go | 80 +++++++++++++++++++++++++ pkg/command/enum_test.go | 72 ++++++++++++++++++++++ pkg/command/form.go | 119 +++++++++++++++++++++++++++---------- pkg/command/form_test.go | 35 +++++++++-- pkg/command/inputs.go | 29 ++++++++- pkg/command/inputs_test.go | 34 +++++++++++ pkg/tui/filters.go | 10 +--- 8 files changed, 344 insertions(+), 52 deletions(-) create mode 100644 pkg/command/enum.go create mode 100644 pkg/command/enum_test.go diff --git a/cmd/logs.go b/cmd/logs.go index c40a879..3de8293 100644 --- a/cmd/logs.go +++ b/cmd/logs.go @@ -117,6 +117,15 @@ func getLogsOptions(ctx context.Context, breadcrumb string) []tui.CustomOption { } func init() { + directionFlag := command.NewEnumInput([]string{"backward", "forward"}, false) + levelFlag := command.NewEnumInput([]string{ + "debug", "info", "notice", "warning", "error", "critical", "alert", "emergency", + }, true) + logTypeFlag := command.NewEnumInput([]string{"app", "request", "build"}, true) + methodTypeFlag := command.NewEnumInput([]string{ + "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD", "CONNECT", "TRACE", + }, true) + logsCmd.RunE = func(cmd *cobra.Command, args []string) error { var input views.LogInput err := command.ParseCommand(cmd, args, &input) @@ -148,14 +157,14 @@ func init() { logsCmd.Flags().String("start", "", "The start time of the logs to query") logsCmd.Flags().String("end", "", "The end time of the logs to query") logsCmd.Flags().StringSlice("text", []string{}, "A list of comma separated strings to search for in the logs") - logsCmd.Flags().StringSlice("level", []string{}, "A list of comma separated log levels to query") - logsCmd.Flags().StringSlice("type", []string{}, "A list of comma separated log types to query") + logsCmd.Flags().Var(levelFlag, "level", "A list of comma separated log levels to query") + logsCmd.Flags().Var(logTypeFlag, "type", "A list of comma separated log types to query") logsCmd.Flags().StringSlice("instance", []string{}, "A list of comma separated instance IDs to query") logsCmd.Flags().StringSlice("host", []string{}, "A list of comma separated hosts to query") logsCmd.Flags().StringSlice("status-code", []string{}, "A list of comma separated status codes to query") - logsCmd.Flags().StringSlice("method", []string{}, "A list of comma separated HTTP methods to query") + logsCmd.Flags().Var(methodTypeFlag, "method", "A list of comma separated HTTP methods to query") logsCmd.Flags().StringSlice("path", []string{}, "A list of comma separated paths to query") logsCmd.Flags().Int("limit", 100, "The maximum number of logs to return") - logsCmd.Flags().String("direction", "backward", "The direction to query the logs. Can be 'forward' or 'backward'") + logsCmd.Flags().Var(directionFlag, "direction", "The direction to query the logs. Can be 'forward' or 'backward'") logsCmd.Flags().Bool("tail", false, "Stream new logs") } diff --git a/pkg/command/enum.go b/pkg/command/enum.go new file mode 100644 index 0000000..b5213c6 --- /dev/null +++ b/pkg/command/enum.go @@ -0,0 +1,80 @@ +package command + +import ( + "fmt" + "strings" +) + +const ( + EnumType = "enum" +) + +type CobraEnum struct { + values []string + selectedIndexes []int + isMultiSelect bool +} + +func NewEnumInput(values []string, isMultiSelect bool) *CobraEnum { + return &CobraEnum{ + values: values, + isMultiSelect: isMultiSelect, + } +} + +func (e *CobraEnum) String() string { + if len(e.values) == 0 && !e.isMultiSelect { + return "Invalid enum value" + } + + values := make([]string, len(e.selectedIndexes)) + for i, index := range e.selectedIndexes { + values[i] = e.values[index] + } + + return strings.Join(values, ", ") +} + +func (e *CobraEnum) Set(v string) error { + values := strings.Split(v, ",") + + for _, splitValue := range values { + for i, value := range e.values { + if strings.EqualFold(splitValue, value) { + e.selectedIndexes = append(e.selectedIndexes, i) + } + } + } + + if len(e.selectedIndexes) != 0 { + return nil + } + + var stringValues []string + + for _, value := range e.values { + stringValues = append(stringValues, fmt.Sprintf("%q", value)) + } + + return fmt.Errorf("must be one of %s", strings.Join(stringValues, ", ")) +} + +func (e *CobraEnum) Type() string { + return EnumType +} + +func (e *CobraEnum) Values() []string { + return e.values +} + +func (e *CobraEnum) SelectedValues() []string { + var selectedValues []string + for _, index := range e.selectedIndexes { + selectedValues = append(selectedValues, e.values[index]) + } + return selectedValues +} + +func (e *CobraEnum) IsMultiSelect() bool { + return e.isMultiSelect +} diff --git a/pkg/command/enum_test.go b/pkg/command/enum_test.go new file mode 100644 index 0000000..009a677 --- /dev/null +++ b/pkg/command/enum_test.go @@ -0,0 +1,72 @@ +package command_test + +import ( + "testing" + + "github.com/renderinc/cli/pkg/command" + "github.com/stretchr/testify/require" +) + +func TestCobraEnum(t *testing.T) { + t.Run("single select", func(t *testing.T) { + t.Run("properties", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, false) + + require.False(t, enum.IsMultiSelect()) + require.Equal(t, "enum", enum.Type()) + require.Equal(t, []string{"a", "b", "c"}, enum.Values()) + }) + + t.Run("can set to valid value", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, false) + + require.NoError(t, enum.Set("b")) + require.Equal(t, []string{"b"}, enum.SelectedValues()) + }) + + t.Run("is case insensitive", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, false) + + require.NoError(t, enum.Set("B")) + require.Equal(t, []string{"b"}, enum.SelectedValues()) + }) + + t.Run("errors when set to invalid value", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, false) + + require.Error(t, enum.Set("d")) + require.Empty(t, enum.SelectedValues()) + }) + }) + + t.Run("multi select", func(t *testing.T) { + t.Run("properties", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, true) + + require.True(t, enum.IsMultiSelect()) + require.Equal(t, "enum", enum.Type()) + require.Equal(t, []string{"a", "b", "c"}, enum.Values()) + }) + + t.Run("can set to valid value", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, true) + + require.NoError(t, enum.Set("b,c")) + require.Equal(t, []string{"b", "c"}, enum.SelectedValues()) + }) + + t.Run("is case insensitive", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, true) + + require.NoError(t, enum.Set("B,C")) + require.Equal(t, []string{"b", "c"}, enum.SelectedValues()) + }) + + t.Run("errors when set to invalid value", func(t *testing.T) { + enum := command.NewEnumInput([]string{"a", "b", "c"}, true) + + require.Error(t, enum.Set("d")) + require.Empty(t, enum.SelectedValues()) + }) + }) +} diff --git a/pkg/command/form.go b/pkg/command/form.go index ac10bd3..97b518f 100644 --- a/pkg/command/form.go +++ b/pkg/command/form.go @@ -6,15 +6,41 @@ import ( "strconv" "strings" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/huh" "github.com/charmbracelet/x/ansi" + "github.com/renderinc/cli/pkg/pointers" "github.com/spf13/cobra" "github.com/spf13/pflag" - - "github.com/renderinc/cli/pkg/pointers" ) -type FormValues map[string]*string +type FormValue interface { + String() string +} + +type stringFormValue string + +func (s *stringFormValue) String() string { + return (string)(*s) +} + +func NewStringFormValue(s string) *stringFormValue { + return (*stringFormValue)(&s) +} + +type stringSliceFormValue []string + +func NewStringSliceFormValue(s string) *stringSliceFormValue { + slice := strings.Split(s, ",") + return (*stringSliceFormValue)(&slice) +} + +func (s *stringSliceFormValue) String() string { + str := strings.Join(*s, ",") + return str +} + +type FormValues map[string]FormValue func FormValuesFromStruct(v any) FormValues { if reflect.TypeOf(v).Kind() != reflect.Ptr { @@ -37,64 +63,64 @@ func FormValuesFromStruct(v any) FormValues { switch field.Type.Kind() { case reflect.Ptr: if elemField.IsNil() { - formValues[cliTag] = pointers.From("") + formValues[cliTag] = NewStringFormValue("") } switch field.Type.Elem().Kind() { case reflect.String: val := elemField.Interface().(*string) - formValues[cliTag] = val + formValues[cliTag] = NewStringFormValue(*val) case reflect.Int: val := elemField.Interface().(*int) - formValues[cliTag] = pointers.From(fmt.Sprintf("%d", *val)) + formValues[cliTag] = NewStringFormValue(fmt.Sprintf("%d", *val)) case reflect.Float64: val := elemField.Interface().(*float64) - formValues[cliTag] = pointers.From(fmt.Sprintf("%f", *val)) + formValues[cliTag] = NewStringFormValue(fmt.Sprintf("%f", *val)) case reflect.Bool: val := elemField.Interface().(*bool) - formValues[cliTag] = pointers.From(fmt.Sprintf("%t", *val)) + formValues[cliTag] = NewStringFormValue(fmt.Sprintf("%t", *val)) } case reflect.Slice: switch field.Type.Elem().Kind() { case reflect.String: val := elemField.Interface().([]string) - formValues[cliTag] = pointers.From(strings.Join(val, ",")) + formValues[cliTag] = NewStringFormValue(strings.Join(val, ",")) case reflect.Int: val := elemField.Interface().([]int) var strs []string for _, v := range val { strs = append(strs, fmt.Sprintf("%d", v)) } - formValues[cliTag] = pointers.From(strings.Join(strs, ",")) + formValues[cliTag] = NewStringFormValue(strings.Join(strs, ",")) case reflect.Float64: val := elemField.Interface().([]float64) var strs []string for _, v := range val { strs = append(strs, fmt.Sprintf("%f", v)) } - formValues[cliTag] = pointers.From(strings.Join(strs, ",")) + formValues[cliTag] = NewStringFormValue(strings.Join(strs, ",")) case reflect.Bool: val := elemField.Interface().([]bool) var strs []string for _, v := range val { strs = append(strs, fmt.Sprintf("%t", v)) } - formValues[cliTag] = pointers.From(strings.Join(strs, ",")) + formValues[cliTag] = NewStringFormValue(strings.Join(strs, ",")) default: panic(fmt.Sprintf("unsupported slice type: %s", field.Type.Elem().Kind())) } case reflect.String: val := elemField.Interface().(string) - formValues[cliTag] = &val + formValues[cliTag] = NewStringFormValue(val) case reflect.Bool: val := elemField.Interface().(bool) - formValues[cliTag] = pointers.From(fmt.Sprintf("%t", val)) + formValues[cliTag] = NewStringFormValue(fmt.Sprintf("%t", val)) case reflect.Int: val := elemField.Interface().(int) - formValues[cliTag] = pointers.From(fmt.Sprintf("%d", val)) + formValues[cliTag] = NewStringFormValue(fmt.Sprintf("%d", val)) case reflect.Float64: val := elemField.Interface().(float64) - formValues[cliTag] = pointers.From(fmt.Sprintf("%f", val)) + formValues[cliTag] = NewStringFormValue(fmt.Sprintf("%f", val)) case reflect.Struct: // skip nested structs continue @@ -139,13 +165,13 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - elemField.Set(reflect.ValueOf(val)) + elemField.Set(reflect.ValueOf(pointers.From(val.String()))) case reflect.Int: val, ok := formValues[cliTag] if !ok { continue } - intVal, err := strconv.Atoi(*val) + intVal, err := strconv.Atoi(val.String()) if err != nil { return err } @@ -155,7 +181,7 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - floatVal, err := strconv.ParseFloat(*val, 64) + floatVal, err := strconv.ParseFloat(val.String(), 64) if err != nil { return err } @@ -165,7 +191,7 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - boolVal, err := strconv.ParseBool(*val) + boolVal, err := strconv.ParseBool(val.String()) if err != nil { return err } @@ -180,14 +206,14 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - elemField.Set(reflect.ValueOf(arrayFromString(*val))) + elemField.Set(reflect.ValueOf(arrayFromString(val.String()))) case reflect.Int: val, ok := formValues[cliTag] if !ok { continue } var intVals []int - for _, v := range arrayFromString(*val) { + for _, v := range arrayFromString(val.String()) { intVal, err := strconv.Atoi(v) if err != nil { return err @@ -201,7 +227,7 @@ func StructFromFormValues(formValues FormValues, v any) error { continue } var floatVals []float64 - for _, v := range arrayFromString(*val) { + for _, v := range arrayFromString(val.String()) { floatVal, err := strconv.ParseFloat(v, 64) if err != nil { return err @@ -215,7 +241,7 @@ func StructFromFormValues(formValues FormValues, v any) error { continue } var boolVals []bool - for _, v := range arrayFromString(*val) { + for _, v := range arrayFromString(val.String()) { boolVal, err := strconv.ParseBool(v) if err != nil { return err @@ -231,13 +257,13 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - elemField.SetString(*val) + elemField.SetString(val.String()) case reflect.Bool: val, ok := formValues[cliTag] if !ok { continue } - boolVal, err := strconv.ParseBool(*val) + boolVal, err := strconv.ParseBool(val.String()) if err != nil { return err } @@ -247,7 +273,7 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - intVal, err := strconv.Atoi(*val) + intVal, err := strconv.Atoi(val.String()) if err != nil { return err } @@ -257,7 +283,7 @@ func StructFromFormValues(formValues FormValues, v any) error { if !ok { continue } - floatVal, err := strconv.ParseFloat(*val, 64) + floatVal, err := strconv.ParseFloat(val.String(), 64) if err != nil { return err } @@ -282,8 +308,9 @@ func HuhForm(cmd *cobra.Command, v any) (*huh.Form, FormValues) { } value := formValues[flag.Name] + if value == nil { - value = pointers.From(flag.DefValue) + value = NewStringFormValue(flag.DefValue) } // We have to wrap the description because of this bug in lipgloss: https://github.com/charmbracelet/lipgloss/issues/85 @@ -291,8 +318,31 @@ func HuhForm(cmd *cobra.Command, v any) (*huh.Form, FormValues) { // filter component. We can adjust if needed. wrappedDescription := ansi.Wrap(flag.Usage, 55, "-") - huhFieldMap[flag.Name] = huh.NewInput().Key(flag.Name).Title(flag.Name).Description(wrappedDescription).Value(value) - formValues[flag.Name] = value + if flag.Value.Type() == EnumType { + enumFlag := flag.Value.(*CobraEnum) + + var options []huh.Option[string] + for _, val := range enumFlag.Values() { + options = append(options, huh.NewOption[string](val, val)) + } + + if enumFlag.IsMultiSelect() { + sliceValue := NewStringSliceFormValue(value.String()) + formValues[flag.Name] = sliceValue + + huhFieldMap[flag.Name] = huh.NewMultiSelect[string]().Key(flag.Name).Title(flag.Name).Description(wrappedDescription).Options(options...).Value((*[]string)(sliceValue)) + } else { + strValue := NewStringFormValue(value.String()) + formValues[flag.Name] = strValue + + huhFieldMap[flag.Name] = huh.NewSelect[string]().Key(flag.Name).Title(flag.Name).Description(wrappedDescription).Options(options...).Value((*string)(strValue)) + } + } else { + strValue := NewStringFormValue(value.String()) + formValues[flag.Name] = strValue + + huhFieldMap[flag.Name] = huh.NewInput().Key(flag.Name).Title(flag.Name).Description(wrappedDescription).Value((*string)(strValue)) + } }) // Order the fields in the form by the order they have in the struct @@ -315,5 +365,10 @@ func HuhForm(cmd *cobra.Command, v any) (*huh.Form, FormValues) { return huh.NewForm(), formValues } - return huh.NewForm(huh.NewGroup(huhFields...)), formValues + keyMap := huh.NewDefaultKeyMap() + keyMap.Input.Next = key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "next")) + keyMap.Select.Next = key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "next")) + keyMap.MultiSelect.Next = key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "next")) + + return huh.NewForm(huh.NewGroup(huhFields...)).WithKeyMap(keyMap), formValues } diff --git a/pkg/command/form_test.go b/pkg/command/form_test.go index 3eacced..a80b454 100644 --- a/pkg/command/form_test.go +++ b/pkg/command/form_test.go @@ -15,7 +15,7 @@ func TestFormValuesFromStruct(t *testing.T) { } v := testStruct{OwnerID: "owner-id"} formValues := command.FormValuesFromStruct(&v) - require.Equal(t, "owner-id", *formValues["owner"]) + require.Equal(t, "owner-id", formValues["owner"].String()) }) t.Run("converts pointer type", func(t *testing.T) { @@ -25,7 +25,7 @@ func TestFormValuesFromStruct(t *testing.T) { ownerID := "owner-id" v := testStruct{OwnerID: &ownerID} formValues := command.FormValuesFromStruct(&v) - require.Equal(t, "owner-id", *formValues["owner"]) + require.Equal(t, "owner-id", formValues["owner"].String()) }) t.Run("converts slice type", func(t *testing.T) { @@ -34,7 +34,7 @@ func TestFormValuesFromStruct(t *testing.T) { } v := testStruct{OwnerIDs: []string{"owner-id-1", "owner-id-2"}} formValues := command.FormValuesFromStruct(&v) - require.Equal(t, "owner-id-1,owner-id-2", *formValues["owners"]) + require.Equal(t, "owner-id-1,owner-id-2", formValues["owners"].String()) }) } @@ -45,7 +45,7 @@ func TestStructFromFormValues(t *testing.T) { type testStruct struct { OwnerID string `cli:"owner"` } - formValues := command.FormValues{"owner": &str} + formValues := command.FormValues{"owner": command.NewStringFormValue(str)} v := testStruct{} require.NoError(t, command.StructFromFormValues(formValues, &v)) require.Equal(t, "owner-id", v.OwnerID) @@ -55,7 +55,7 @@ func TestStructFromFormValues(t *testing.T) { type testStruct struct { OwnerID *string `cli:"owner"` } - formValues := command.FormValues{"owner": &str} + formValues := command.FormValues{"owner": command.NewStringFormValue(str)} v := testStruct{} require.NoError(t, command.StructFromFormValues(formValues, &v)) require.Equal(t, "owner-id", *v.OwnerID) @@ -66,7 +66,7 @@ func TestStructFromFormValues(t *testing.T) { OwnerIDs []string `cli:"owners"` } strSlice := "owner-id-1,owner-id-2" - formValues := command.FormValues{"owners": &strSlice} + formValues := command.FormValues{"owners": command.NewStringSliceFormValue(strSlice)} v := testStruct{} require.NoError(t, command.StructFromFormValues(formValues, &v)) require.Equal(t, []string{"owner-id-1", "owner-id-2"}, v.OwnerIDs) @@ -90,4 +90,27 @@ func TestHuhForm(t *testing.T) { require.Contains(t, form.View(), "foo") require.Contains(t, form.View(), "bar") }) + + t.Run("creates form with enums", func(t *testing.T) { + type testStruct struct { + Foo string `cli:"foo"` + Bar int `cli:"bar"` + } + v := testStruct{} + cmd := cobra.Command{} + + // foo is multi select + fooInput := command.NewEnumInput([]string{"multi choice 1", "multi choice 2", "multi choice 3"}, true) + cmd.Flags().Var(fooInput, "foo", "") + + // bar is single select + barInput := command.NewEnumInput([]string{"single choice 1", "single choice 2", "single choice 3"}, false) + cmd.Flags().Var(barInput, "bar", "") + + form, _ := command.HuhForm(&cmd, &v) + form.Init() + + require.Contains(t, form.View(), "multi choice 3") + require.Contains(t, form.View(), "single choice 2") + }) } diff --git a/pkg/command/inputs.go b/pkg/command/inputs.go index cd6aacf..17a7a6e 100644 --- a/pkg/command/inputs.go +++ b/pkg/command/inputs.go @@ -43,6 +43,13 @@ func getStringValue(flags *pflag.FlagSet, args []string, tag string) (*string, e } } + if flag := flags.Lookup(tag); flag != nil { + if flag.Value.Type() == EnumType { + val := flag.Value.String() + return &val, nil + } + } + val, err := flags.GetString(tag) if err != nil { return nil, err @@ -50,6 +57,26 @@ func getStringValue(flags *pflag.FlagSet, args []string, tag string) (*string, e return &val, nil } +func getStringSliceValue(flags *pflag.FlagSet, tag string) ([]string, error) { + if flag := flags.Lookup(tag); flag != nil { + if flag.Value.Type() == EnumType { + cobraEnum, ok := flag.Value.(*CobraEnum) + if !ok { + return nil, fmt.Errorf("unexpected enum type") + } + + val := cobraEnum.SelectedValues() + return val, nil + } + } + + val, err := flags.GetStringSlice(tag) + if err != nil { + return nil, err + } + return val, nil +} + func getIntValue(flags *pflag.FlagSet, args []string, tag string) (*int, error) { if isArg(tag) { if val, err := getArgValue(tag, args); err != nil { @@ -178,7 +205,7 @@ func ParseCommand(cmd *cobra.Command, args []string, v any) error { case reflect.Slice: switch field.Type.Elem().Kind() { case reflect.String: - val, err := flags.GetStringSlice(cliTag) + val, err := getStringSliceValue(flags, cliTag) if err != nil { return err } diff --git a/pkg/command/inputs_test.go b/pkg/command/inputs_test.go index 0e0e04b..aad4aad 100644 --- a/pkg/command/inputs_test.go +++ b/pkg/command/inputs_test.go @@ -25,6 +25,40 @@ func TestParseCommand(t *testing.T) { require.Equal(t, "bar", v.Foo) }) + t.Run("parse single select enum", func(t *testing.T) { + enumInput := command.NewEnumInput([]string{"bar", "baz"}, false) + + type testStruct struct { + Foo string `cli:"foo"` + } + var v testStruct + cmd := &cobra.Command{} + cmd.Flags().Var(enumInput, "foo", "") + require.NoError(t, cmd.ParseFlags([]string{"--foo", "baz"})) + + err := command.ParseCommand(cmd, []string{}, &v) + require.NoError(t, err) + + require.Equal(t, "baz", v.Foo) + }) + + t.Run("parse multi select enum", func(t *testing.T) { + enumInput := command.NewEnumInput([]string{"a", "b", "c"}, true) + + type testStruct struct { + Foo []string `cli:"foo"` + } + var v testStruct + cmd := &cobra.Command{} + cmd.Flags().Var(enumInput, "foo", "") + require.NoError(t, cmd.ParseFlags([]string{"--foo", "a,c"})) + + err := command.ParseCommand(cmd, []string{}, &v) + require.NoError(t, err) + + require.Equal(t, []string{"a", "c"}, v.Foo) + }) + t.Run("parse pointer", func(t *testing.T) { type testStruct struct { Foo *string `cli:"foo"` diff --git a/pkg/tui/filters.go b/pkg/tui/filters.go index 8f0270f..fe71087 100644 --- a/pkg/tui/filters.go +++ b/pkg/tui/filters.go @@ -1,7 +1,6 @@ package tui import ( - "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" @@ -17,14 +16,7 @@ type FilterModel struct { func NewFilterModel(form *huh.Form, search func(*huh.Form) tea.Cmd) *FilterModel { return &FilterModel{ - form: form.WithKeyMap(&huh.KeyMap{ - Input: huh.InputKeyMap{ - AcceptSuggestion: key.NewBinding(key.WithKeys("ctrl+e"), key.WithHelp("ctrl+e", "complete")), - Prev: key.NewBinding(key.WithKeys("shift+tab"), key.WithHelp("shift+tab", "back")), - Next: key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "next")), - Submit: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "to submit")), - }, - }), + form: form, search: search, } }