Skip to content

Commit 99d8ded

Browse files
authored
refactor(keybindings): Use bubble tea keybindings (#47)
1 parent c65754f commit 99d8ded

File tree

8 files changed

+109
-92
lines changed

8 files changed

+109
-92
lines changed

internal/app/helper.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"strings"
66

7+
"github.com/charmbracelet/bubbles/key"
78
"github.com/charmbracelet/bubbles/table"
89
tea "github.com/charmbracelet/bubbletea"
910
"github.com/charmbracelet/lipgloss"
@@ -110,15 +111,13 @@ func (h helper) handleOpenJSONRowRequestedMsg(
110111

111112
func (h helper) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
112113
switch {
113-
case h.isQuitKeyMap(msg):
114+
case key.Matches(msg, defaultKeys.Exit):
114115
return tea.Quit
115-
case h.isBackKeyMap(msg):
116-
return events.BackKeyClicked
117-
case h.isFilterKeyMap(msg):
116+
case key.Matches(msg, defaultKeys.Filter):
118117
return events.FilterKeyClicked
119-
case h.isEnterKeyMap(msg):
118+
case key.Matches(msg, defaultKeys.ToggleView):
120119
return events.EnterKeyClicked
121-
case h.isArrowRightKeyMap(msg):
120+
case key.Matches(msg, defaultKeys.ToggleViewArrow):
122121
return events.ArrowRightKeyClicked
123122
default:
124123
return nil

internal/app/keymap.go

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,54 @@
11
package app
22

3-
import tea "github.com/charmbracelet/bubbletea"
3+
import "github.com/charmbracelet/bubbles/key"
44

5-
func (a Application) isQuitKeyMap(
6-
msg tea.KeyMsg,
7-
) bool {
8-
switch msg.String() {
9-
case "ctrl+c", "f10":
10-
return true
11-
default:
12-
return false
13-
}
14-
}
15-
16-
func (a Application) isEnterKeyMap(msg tea.KeyMsg) bool {
17-
return msg.String() == "enter"
18-
}
19-
20-
func (a Application) isArrowUpKeyMap(msg tea.KeyMsg) bool {
21-
return msg.Type == tea.KeyUp
5+
type KeyMap struct {
6+
Exit key.Binding
7+
Back key.Binding
8+
ToggleView key.Binding
9+
ToggleViewArrow key.Binding
10+
Up key.Binding
11+
Down key.Binding
12+
Filter key.Binding
2213
}
2314

24-
func (a Application) isArrowRightKeyMap(msg tea.KeyMsg) bool {
25-
return msg.Type == tea.KeyRight
15+
var defaultKeys = KeyMap{
16+
Exit: key.NewBinding(
17+
key.WithKeys("ctrl+c", "f10"),
18+
key.WithHelp("Ctrl+C", "Exit"),
19+
),
20+
Back: key.NewBinding(
21+
key.WithKeys("esc", "q"),
22+
key.WithHelp("Esc", "Back"),
23+
),
24+
ToggleView: key.NewBinding(
25+
key.WithKeys("enter"),
26+
key.WithHelp("Enter", "Open/Hide"),
27+
),
28+
ToggleViewArrow: key.NewBinding(
29+
key.WithKeys("right"),
30+
),
31+
Up: key.NewBinding(
32+
key.WithKeys("up"),
33+
key.WithHelp("↑", "Up"),
34+
),
35+
Down: key.NewBinding(
36+
key.WithKeys("down"),
37+
key.WithHelp("↓", "Down"),
38+
),
39+
Filter: key.NewBinding(
40+
key.WithKeys("f"),
41+
key.WithHelp("F", "Filter"),
42+
),
2643
}
2744

28-
func (a Application) isFilterKeyMap(msg tea.KeyMsg) bool {
29-
return msg.String() == "f"
45+
func (k KeyMap) ShortHelp() []key.Binding {
46+
return []key.Binding{k.Exit, k.Back, k.ToggleView, k.Up, k.Down, k.Filter}
3047
}
3148

32-
func (a Application) isBackKeyMap(msg tea.KeyMsg) bool {
33-
switch msg.String() {
34-
case "esc", "q":
35-
return true
36-
default:
37-
return false
49+
func (k KeyMap) FullHelp() [][]key.Binding {
50+
return [][]key.Binding{
51+
{k.Back, k.Up, k.Down}, // first column
52+
{k.ToggleView, k.Exit, k.Filter}, // second column
3853
}
3954
}

internal/app/statefiltered.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package app
22

33
import (
4+
"github.com/charmbracelet/bubbles/key"
45
tea "github.com/charmbracelet/bubbletea"
56

67
"github.com/hedhyw/json-log-viewer/internal/pkg/events"
@@ -16,6 +17,7 @@ type StateFiltered struct {
1617
logEntries source.LogEntries
1718

1819
filterText string
20+
keys KeyMap
1921
}
2022

2123
func newStateFiltered(
@@ -30,6 +32,7 @@ func newStateFiltered(
3032
table: previousState.table,
3133

3234
filterText: filterText,
35+
keys: defaultKeys,
3336
}
3437
}
3538

@@ -58,17 +61,19 @@ func (s StateFiltered) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5861
switch msg := msg.(type) {
5962
case events.ErrorOccuredMsg:
6063
return s.handleErrorOccuredMsg(msg)
61-
case events.BackKeyClickedMsg:
62-
return s.previousState.withApplication(s.Application)
63-
case events.FilterKeyClickedMsg:
64-
return s.handleFilterKeyClickedMsg()
65-
case events.EnterKeyClickedMsg, events.ArrowRightKeyClickedMsg:
66-
return s.handleRequestOpenJSON()
6764
case events.LogEntriesLoadedMsg:
6865
return s.handleLogEntriesLoadedMsg(msg)
6966
case events.OpenJSONRowRequestedMsg:
7067
return s.handleOpenJSONRowRequestedMsg(msg, s)
7168
case tea.KeyMsg:
69+
switch {
70+
case key.Matches(msg, s.keys.Back):
71+
return s.previousState.withApplication(s.Application)
72+
case key.Matches(msg, s.keys.Filter):
73+
return s.handleFilterKeyClickedMsg()
74+
case key.Matches(msg, s.keys.ToggleViewArrow), key.Matches(msg, s.keys.ToggleView):
75+
return s.handleRequestOpenJSON()
76+
}
7277
if cmd := s.handleKeyMsg(msg); cmd != nil {
7378
return s, cmd
7479
}

internal/app/statefiltering.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package app
22

33
import (
4+
"github.com/charmbracelet/bubbles/key"
45
"github.com/charmbracelet/bubbles/textinput"
56
tea "github.com/charmbracelet/bubbletea"
67

@@ -15,6 +16,7 @@ type StateFiltering struct {
1516
table logsTableModel
1617

1718
textInput textinput.Model
19+
keys KeyMap
1820
}
1921

2022
func newStateFiltering(
@@ -31,6 +33,7 @@ func newStateFiltering(
3133
table: previousState.table,
3234

3335
textInput: textInput,
36+
keys: defaultKeys,
3437
}
3538
}
3639

@@ -53,11 +56,13 @@ func (s StateFiltering) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5356
switch msg := msg.(type) {
5457
case events.ErrorOccuredMsg:
5558
return s.handleErrorOccuredMsg(msg)
56-
case events.BackKeyClickedMsg:
57-
return s.previousState.withApplication(s.Application)
58-
case events.EnterKeyClickedMsg:
59-
return s.handleEnterKeyClickedMsg()
6059
case tea.KeyMsg:
60+
switch {
61+
case key.Matches(msg, s.keys.Back):
62+
return s.previousState.withApplication(s.Application)
63+
case key.Matches(msg, s.keys.ToggleView):
64+
return s.handleEnterKeyClickedMsg()
65+
}
6166
if cmd := s.handleKeyMsg(msg); cmd != nil {
6267
// Intercept table update.
6368
return s, cmd

internal/app/stateloaded.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package app
22

33
import (
4+
"github.com/charmbracelet/bubbles/help"
5+
"github.com/charmbracelet/bubbles/key"
46
tea "github.com/charmbracelet/bubbletea"
57

68
"github.com/hedhyw/json-log-viewer/internal/pkg/events"
79
"github.com/hedhyw/json-log-viewer/internal/pkg/source"
810
)
911

10-
const defaultFooter = "[Ctrl+C] Exit; [Esc] Back; [Enter] Open/Hide; [↑↓] Navigation; [F] Filter"
11-
1212
// StateLoaded is a state that shows all loaded records.
1313
type StateLoaded struct {
1414
helper
@@ -17,6 +17,9 @@ type StateLoaded struct {
1717

1818
table logsTableModel
1919
logEntries source.LogEntries
20+
21+
keys KeyMap
22+
help help.Model
2023
}
2124

2225
func newStateViewLogs(application Application, logEntries source.LogEntries) StateLoaded {
@@ -29,6 +32,9 @@ func newStateViewLogs(application Application, logEntries source.LogEntries) Sta
2932

3033
table: table,
3134
logEntries: logEntries,
35+
36+
keys: defaultKeys,
37+
help: help.New(),
3238
}
3339
}
3440

@@ -39,15 +45,15 @@ func (s StateLoaded) Init() tea.Cmd {
3945

4046
// View renders component. It implements tea.Model.
4147
func (s StateLoaded) View() string {
42-
return s.viewTable() + "\n" + s.viewFooter()
48+
return s.viewTable() + s.viewHelp()
4349
}
4450

4551
func (s StateLoaded) viewTable() string {
4652
return s.BaseStyle.Render(s.table.View())
4753
}
4854

49-
func (s StateLoaded) viewFooter() string {
50-
return s.FooterStyle.Render(defaultFooter)
55+
func (s StateLoaded) viewHelp() string {
56+
return "\n" + s.help.View(s.keys)
5157
}
5258

5359
// Update handles events. It implements tea.Model.
@@ -67,19 +73,16 @@ func (s StateLoaded) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
6773
return s.handleViewRowsReloadRequestedMsg()
6874
case events.OpenJSONRowRequestedMsg:
6975
return s.handleOpenJSONRowRequestedMsg(msg, s)
70-
case events.BackKeyClickedMsg:
71-
return s, tea.Quit
72-
case events.EnterKeyClickedMsg, events.ArrowRightKeyClickedMsg:
73-
return s.handleRequestOpenJSON()
74-
case events.FilterKeyClickedMsg:
75-
return s.handleFilterKeyClickedMsg()
7676
case tea.KeyMsg:
77-
cmdBatch = append(cmdBatch, s.handleKeyMsg(msg)...)
78-
79-
if s.isFilterKeyMap(msg) {
80-
// Intercept table update.
81-
return s, tea.Batch(cmdBatch...)
77+
switch {
78+
case key.Matches(msg, s.keys.Back):
79+
return s, tea.Quit
80+
case key.Matches(msg, s.keys.Filter):
81+
return s.handleFilterKeyClickedMsg()
82+
case key.Matches(msg, s.keys.ToggleViewArrow), key.Matches(msg, s.keys.ToggleView):
83+
return s.handleRequestOpenJSON()
8284
}
85+
cmdBatch = append(cmdBatch, s.handleKeyMsg(msg)...)
8386
}
8487

8588
s.table, cmdBatch = batched(s.table.Update(msg))(cmdBatch)
@@ -92,7 +95,7 @@ func (s StateLoaded) handleKeyMsg(msg tea.KeyMsg) []tea.Cmd {
9295

9396
cmdBatch = appendCmd(cmdBatch, s.helper.handleKeyMsg(msg))
9497

95-
if s.isArrowUpKeyMap(msg) {
98+
if key.Matches(msg, s.keys.Up) {
9699
cmdBatch = appendCmd(cmdBatch, s.handleArrowUpKeyClicked())
97100
}
98101

internal/app/stateloaded_test.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ func TestStateLoadedQuit(t *testing.T) {
7272
t.Parallel()
7373

7474
_, cmd := model.Update(tea.KeyMsg{Type: tea.KeyEsc})
75-
requireCmdMsg(t, events.BackKeyClickedMsg{}, cmd)
76-
77-
_, cmd = model.Update(events.BackKeyClickedMsg{})
7875
requireCmdMsg(t, tea.Quit(), cmd)
7976
})
8077

@@ -85,9 +82,6 @@ func TestStateLoadedQuit(t *testing.T) {
8582
Type: tea.KeyRunes,
8683
Runes: []rune{'q'},
8784
})
88-
requireCmdMsg(t, events.BackKeyClickedMsg{}, cmd)
89-
90-
_, cmd = model.Update(events.BackKeyClickedMsg{})
9185
requireCmdMsg(t, tea.Quit(), cmd)
9286
})
9387

internal/app/stateviewrow.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package app
22

33
import (
4+
"github.com/charmbracelet/bubbles/key"
45
tea "github.com/charmbracelet/bubbletea"
56

67
"github.com/hedhyw/json-log-viewer/internal/pkg/events"
@@ -17,6 +18,8 @@ type StateViewRow struct {
1718

1819
logEntry source.LogEntry
1920
jsonView tea.Model
21+
22+
keys KeyMap
2023
}
2124

2225
func newStateViewRow(
@@ -34,6 +37,8 @@ func newStateViewRow(
3437

3538
logEntry: logEntry,
3639
jsonView: jsonViewModel,
40+
41+
keys: defaultKeys,
3742
}
3843
}
3944

@@ -56,11 +61,11 @@ func (s StateViewRow) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5661
switch msg := msg.(type) {
5762
case events.ErrorOccuredMsg:
5863
return s.handleErrorOccuredMsg(msg)
59-
case events.BackKeyClickedMsg:
60-
return s.previousState.withApplication(s.Application)
61-
case events.EnterKeyClickedMsg:
62-
return s.previousState.withApplication(s.Application)
6364
case tea.KeyMsg:
65+
if key.Matches(msg, s.keys.Back) || key.Matches(msg, s.keys.ToggleView) {
66+
return s.previousState.withApplication(s.Application)
67+
}
68+
6469
if cmd = s.handleKeyMsg(msg); cmd != nil {
6570
return s, cmd
6671
}
@@ -72,7 +77,7 @@ func (s StateViewRow) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7277
}
7378

7479
func (s StateViewRow) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
75-
if s.isArrowRightKeyMap(msg) {
80+
if key.Matches(msg, s.keys.ToggleViewArrow) {
7681
return nil
7782
}
7883

0 commit comments

Comments
 (0)