From 3ec2b00fe212b6029f486f90d60759e528f6163a Mon Sep 17 00:00:00 2001 From: Steven Kalt Date: Tue, 10 Nov 2020 19:47:54 -0500 Subject: [PATCH] fix(cli): ensure correct help bar shown Also consolidates help-bar construction logic --- cmd/tui.go | 52 +++--------------- .../breaking_change_input.go | 8 ++- pkg/config/cfg.go | 16 +++++- pkg/description_editor/description_editor.go | 10 ++-- pkg/scope_selector/input.go | 55 +++++++++++++++++++ pkg/single_select/select.go | 4 -- pkg/type_selector/input.go | 47 ++++++++++++++++ 7 files changed, 139 insertions(+), 53 deletions(-) create mode 100644 pkg/scope_selector/input.go create mode 100644 pkg/type_selector/input.go diff --git a/cmd/tui.go b/cmd/tui.go index c9e4025..3cc9960 100644 --- a/cmd/tui.go +++ b/cmd/tui.go @@ -6,12 +6,12 @@ import ( "strings" tea "github.com/charmbracelet/bubbletea" - "github.com/muesli/termenv" "github.com/skalt/git-cc/pkg/breaking_change_input" "github.com/skalt/git-cc/pkg/config" "github.com/skalt/git-cc/pkg/description_editor" "github.com/skalt/git-cc/pkg/parser" - "github.com/skalt/git-cc/pkg/single_select" + "github.com/skalt/git-cc/pkg/scope_selector" + "github.com/skalt/git-cc/pkg/type_selector" ) type componentIndex int @@ -32,21 +32,14 @@ var ( type InputComponent interface { View() string Value() string - - // Update(tea.Msg) (tea.Model, tea.Cmd) - // // tea.Model // Init() tea.Cmd, Update(tea.Msg) (tea.Model, tea.Cmd), View() string - // Focus() tea.Cmd // should focus any internals, i.e. text inputs - // // Cancel() // should clean up any resources (i.e. open channels) - // Submit() // send the input to the output channel } type model struct { - // components [done]InputComponent commit [doneIndex]string viewing componentIndex - typeInput single_select.Model - scopeInput single_select.Model + typeInput type_selector.Model + scopeInput scope_selector.Model descriptionInput description_editor.Model breakingChangeInput breaking_change_input.Model @@ -101,19 +94,8 @@ func (m model) currentComponent() InputComponent { // function that returns the initialize function and is typically how you would // pass arguments to a tea.Init function. func initialModel(choice chan string, cc *parser.CC, cfg config.Cfg) model { - typeModel := single_select.NewModel( - termenv.String("select a commit type: ").Faint().String(), // context - cc.Type, // value - cfg.CommitTypes, - ) - scopeModel := single_select.NewModel( - termenv.String("select a scope:").Faint().String(), - cc.Scope, - append( - []map[string]string{{"": "unscoped; affects the entire project"}}, - cfg.Scopes..., - ), - ) // TODO: Option to add new scope? + typeModel := type_selector.NewModel(cc, cfg) + scopeModel := scope_selector.NewModel(cc, cfg) descModel := description_editor.NewModel( cfg.HeaderMaxLength, cc.Description, cfg.EnforceMaxLength, ) @@ -145,7 +127,6 @@ func initialModel(choice chan string, cc *parser.CC, cfg config.Cfg) model { m = m.submit().advance() m.descriptionInput = m.descriptionInput.SetPrefix(m.contextValue()) } - return m } @@ -166,24 +147,9 @@ func (m model) updateCurrentInput(msg tea.Msg) model { func (m model) shouldSkip(component componentIndex) bool { switch component { case commitTypeIndex: - commitType := m.commit[commitTypeIndex] - for _, opt := range m.typeInput.Options { - if commitType == opt { - return true - } - } - return false + return m.typeInput.ShouldSkip(m.commit[commitTypeIndex]) case scopeIndex: - if len(m.scopeInput.Options) == 0 { - return true - } - scope := m.commit[scopeIndex] - for _, opt := range m.scopeInput.Options { - if scope == opt && opt != "" { - return true - } - } - return false + return m.scopeInput.ShouldSkip(m.commit[scopeIndex]) default: return false } @@ -218,7 +184,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.viewing-- } return m, cmd - case tea.KeyEnter: + case tea.KeyEnter, tea.KeyTab: switch m.viewing { default: m = m.submit().advance() diff --git a/pkg/breaking_change_input/breaking_change_input.go b/pkg/breaking_change_input/breaking_change_input.go index ca00c5c..4dff619 100644 --- a/pkg/breaking_change_input/breaking_change_input.go +++ b/pkg/breaking_change_input/breaking_change_input.go @@ -2,6 +2,8 @@ package breaking_change_input // TODO: refactor to a better name ^ import ( + "strings" + "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/muesli/termenv" @@ -12,12 +14,16 @@ type Model struct { input textinput.Model } +var helpBar = termenv.String(strings.Join( + []string{config.HelpSubmit, config.HelpBack, config.HelpCancel}, "; "), +).Faint().String() + func (m Model) Value() string { return m.input.Value() } func (m Model) View() string { - return m.input.View() + "\n\n" + config.HelpBar + return m.input.View() + "\n\n" + helpBar + "\n" } func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { diff --git a/pkg/config/cfg.go b/pkg/config/cfg.go index 41b41f2..4719c7f 100644 --- a/pkg/config/cfg.go +++ b/pkg/config/cfg.go @@ -28,9 +28,23 @@ var ( {"refactor": "changes the code without changing behavior"}, {"revert": "reverts prior changes"}, } - HelpBar = termenv.String("submit: enter; go back: shift+tab; cancel: ctrl+c").Faint().String() ) +const ( + HelpSubmit = "submit: tab/enter" + HelpBack = "go back: shift+tab" + HelpCancel = "cancel: ctrl+c" + HelpSelect = "navigate: up/down" +) + +func Faint(s string) string { + return termenv.String(s).Faint().String() +} + +func HelpBar(s ...string) string { + return Faint(fmt.Sprintf("\n%s", strings.Join(s, "; "))) +} + type Cfg struct { CommitTypes []map[string]string `mapstructure:"commit_types"` Scopes []map[string]string `mapstructure:"scopes"` diff --git a/pkg/description_editor/description_editor.go b/pkg/description_editor/description_editor.go index 398d2b4..0b5588a 100644 --- a/pkg/description_editor/description_editor.go +++ b/pkg/description_editor/description_editor.go @@ -11,6 +11,8 @@ import ( "github.com/skalt/git-cc/pkg/config" ) +var helpBar = config.HelpBar(config.HelpSubmit, config.HelpBack, config.HelpCancel) + const prePrompt = "A short description of the changes:\n\n" type Model struct { @@ -22,7 +24,7 @@ type Model struct { func (m Model) SetPrefix(prefix string) Model { m.prefixLen = len(prefix) - m.input.Prompt = termenv.String(prePrompt).Faint().String() + prefix + m.input.Prompt = config.Faint(prePrompt) + prefix return m } func (m Model) SetErr(err error) Model { @@ -45,7 +47,7 @@ func NewModel(lengthLimit int, value string, enforced bool) Model { input.SetValue(value) input.SetCursor(len(value)) // input.Cursor = len(value) - input.Prompt = termenv.String(prePrompt).Faint().String() + input.Prompt = config.Faint(prePrompt) if enforced { input.CharLimit = lengthLimit } @@ -62,7 +64,7 @@ func viewCounter(m Model) string { paddedFormat := fmt.Sprintf("(%%%dd/%d)", len(fmt.Sprintf("%d", m.lengthLimit)), m.lengthLimit) view := fmt.Sprintf(paddedFormat, current) if current < m.lengthLimit { - return termenv.String(view).Faint().String() + return config.Faint(view) } else if current == m.lengthLimit { return view // render in a warning color termenv.String(view). } else { @@ -71,7 +73,7 @@ func viewCounter(m Model) string { } func viewHelpBar(m Model) string { - return fmt.Sprintf("\n%s %s", config.HelpBar, viewCounter(m)) + return fmt.Sprintf("\n%s %s", helpBar, viewCounter(m)) } func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { diff --git a/pkg/scope_selector/input.go b/pkg/scope_selector/input.go new file mode 100644 index 0000000..69b73d4 --- /dev/null +++ b/pkg/scope_selector/input.go @@ -0,0 +1,55 @@ +package scope_selector + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/skalt/git-cc/pkg/config" + "github.com/skalt/git-cc/pkg/parser" + "github.com/skalt/git-cc/pkg/single_select" +) + +var helpBar = config.HelpBar( + config.HelpSubmit, config.HelpSelect, config.HelpBack, config.HelpCancel, +) + +type Model struct { + input single_select.Model +} + +func NewModel(cc *parser.CC, cfg config.Cfg) Model { + return Model{ + single_select.NewModel( + config.Faint("select a scope:"), + cc.Scope, + append( + []map[string]string{{"": "unscoped; affects the entire project"}}, + cfg.Scopes..., + ), + ), // TODO: Option to add new scope? + } +} + +func (m Model) Value() string { + return m.input.Value() +} + +func (m Model) View() string { + return m.input.View() + helpBar +} + +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + var cmd tea.Cmd + m.input, cmd = m.input.Update(msg) + return m, cmd +} + +func (m Model) ShouldSkip(currentValue string) bool { + if len(m.input.Options) == 0 { + return true + } + for _, opt := range m.input.Options { + if currentValue == opt && opt != "" { + return true + } + } + return false +} diff --git a/pkg/single_select/select.go b/pkg/single_select/select.go index 084ba71..9dadf06 100644 --- a/pkg/single_select/select.go +++ b/pkg/single_select/select.go @@ -197,9 +197,5 @@ func (m Model) View() string { s.WriteString("\n") } - s.WriteString( - term.String("\n(tab/enter to select, up/down to navigate, Ctrl+C to quit)\n").Faint().String(), - ) - return s.String() } diff --git a/pkg/type_selector/input.go b/pkg/type_selector/input.go new file mode 100644 index 0000000..0cfd8f8 --- /dev/null +++ b/pkg/type_selector/input.go @@ -0,0 +1,47 @@ +package type_selector + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/skalt/git-cc/pkg/config" + "github.com/skalt/git-cc/pkg/parser" + "github.com/skalt/git-cc/pkg/single_select" +) + +var helpBar = config.HelpBar( + config.HelpSubmit, config.HelpSelect, config.HelpCancel, +) + +type Model struct { + input single_select.Model +} + +func NewModel(cc *parser.CC, cfg config.Cfg) Model { + return Model{ + single_select.NewModel( + config.Faint("select a commit type: "), cc.Type, cfg.CommitTypes, + ), + } +} + +func (m Model) Value() string { + return m.input.Value() +} + +func (m Model) View() string { + return m.input.View() + helpBar +} + +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + var cmd tea.Cmd + m.input, cmd = m.input.Update(msg) + return m, cmd +} + +func (m Model) ShouldSkip(currentValue string) bool { + for _, opt := range m.input.Options { + if opt == currentValue { + return true + } + } + return false +}