Skip to content

Commit 95faae7

Browse files
authored
feat: support hot-reload (#16)
1 parent 2fb9dc4 commit 95faae7

File tree

7 files changed

+108
-21
lines changed

7 files changed

+108
-21
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ OUT_BIN?=${PWD}/bin/jlv
33
COVER_PACKAGES=./...
44
VERSION?=${shell git describe --tags}
55

6+
all: lint test build
7+
68
run:
79
@echo "building ${VERSION}"
810
go run ./cmd/jlv assets/example.log

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ jlv file.json
5151
| Esc | Back |
5252
| ↑↓ | Navigation |
5353

54+
> \[\] Click Up on the first row to reload the file.
55+
5456
## Install
5557

5658
### MacOS/Linux HomeBrew

internal/app/app_test.go

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package app_test
22

33
import (
4+
"os"
5+
"strings"
46
"testing"
7+
"unicode/utf8"
58

69
tea "github.com/charmbracelet/bubbletea"
710
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
812

913
"github.com/hedhyw/json-log-viewer/assets"
1014
"github.com/hedhyw/json-log-viewer/internal/app"
@@ -119,26 +123,9 @@ func TestAppQuit(t *testing.T) {
119123
})
120124
}
121125

122-
func newTestModel(tb testing.TB, content []byte) app.Model {
123-
tb.Helper()
124-
125-
testFile := tests.RequireCreateFile(tb, content)
126-
127-
appModel := app.NewModel(testFile)
128-
cmd := appModel.Init()
129-
130-
appModel, _ = toAppModel(appModel.Update(cmd()))
131-
132-
return appModel
133-
}
134-
135-
func toAppModel(teaModel tea.Model, cmd tea.Cmd) (app.Model, tea.Cmd) {
136-
appModel, _ := teaModel.(app.Model)
137-
138-
return appModel, cmd
139-
}
140-
141126
func TestAppViewFiltereRunes(t *testing.T) {
127+
t.Parallel()
128+
142129
appModel := newTestModel(t, assets.ExampleJSONLog())
143130

144131
appModel, _ = toAppModel(appModel.Update(tea.KeyMsg{
@@ -160,3 +147,73 @@ func TestAppViewFiltereRunes(t *testing.T) {
160147
assert.NotEqual(t, tea.Quit(), cmd())
161148
assert.True(t, appModel.IsFilterShown(), appModel.View())
162149
}
150+
151+
func TestAppViewReload(t *testing.T) {
152+
t.Parallel()
153+
154+
const expected = "included"
155+
156+
const (
157+
jsonFile = `
158+
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "test2"}
159+
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "test1"}
160+
`
161+
162+
jsonFileUpdated = `
163+
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "` + expected + `"}
164+
` + jsonFile
165+
)
166+
167+
appModel := newTestModel(t, []byte(jsonFile))
168+
169+
rendered := appModel.View()
170+
assert.NotContains(t, rendered, expected)
171+
172+
err := os.WriteFile(appModel.File(), []byte(jsonFileUpdated), os.ModePerm)
173+
require.NoError(t, err)
174+
175+
appModel, _ = toAppModel(appModel.Update(tea.KeyMsg{
176+
Type: tea.KeyUp,
177+
}))
178+
179+
rendered = appModel.View()
180+
assert.NotContains(t, rendered, expected)
181+
}
182+
183+
func TestAppViewResized(t *testing.T) {
184+
t.Parallel()
185+
186+
appModel := newTestModel(t, assets.ExampleJSONLog())
187+
188+
windowSize := tea.WindowSizeMsg{
189+
Width: 60,
190+
Height: 10,
191+
}
192+
193+
appModel, _ = toAppModel(appModel.Update(windowSize))
194+
195+
rendered := appModel.View()
196+
lines := strings.Split(rendered, "\n")
197+
if assert.NotEmpty(t, lines, rendered) {
198+
assert.Less(t, utf8.RuneCountInString(lines[0]), windowSize.Width, rendered)
199+
}
200+
}
201+
202+
func newTestModel(tb testing.TB, content []byte) app.Model {
203+
tb.Helper()
204+
205+
testFile := tests.RequireCreateFile(tb, content)
206+
207+
appModel := app.NewModel(testFile)
208+
cmd := appModel.Init()
209+
210+
appModel, _ = toAppModel(appModel.Update(cmd()))
211+
212+
return appModel
213+
}
214+
215+
func toAppModel(teaModel tea.Model, cmd tea.Cmd) (app.Model, tea.Cmd) {
216+
appModel, _ := teaModel.(app.Model)
217+
218+
return appModel, cmd
219+
}

internal/app/handler.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import (
99
const cellIDLogLevel = 1
1010

1111
func (m Model) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
12+
for _, key := range m.table.KeyMap.LineUp.Keys() {
13+
if msg.String() == key {
14+
return m.handleUp()
15+
}
16+
}
17+
1218
switch msg.String() {
1319
case "esc":
1420
return m.back()
@@ -26,6 +32,10 @@ func (m Model) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
2632
}
2733

2834
func (m Model) handleEnter() (tea.Model, tea.Cmd) {
35+
if m.IsErrorShown() {
36+
return m.quit()
37+
}
38+
2939
if m.IsFilterShown() {
3040
return m.applyFilter()
3141
}
@@ -37,6 +47,16 @@ func (m Model) handleEnter() (tea.Model, tea.Cmd) {
3747
return nil, nil
3848
}
3949

50+
func (m Model) handleUp() (tea.Model, tea.Cmd) {
51+
if m.table.Cursor() != 0 || !m.IsTableShown() || m.IsFiltered() {
52+
return nil, nil
53+
}
54+
55+
m.allLogEntries = nil
56+
57+
return m, m.Init()
58+
}
59+
4060
func (m Model) handleWindowSizeMsg(msg tea.WindowSizeMsg) Model {
4161
x, y := m.baseStyle.GetFrameSize()
4262
m.table.SetWidth(msg.Width - x*2)
@@ -69,6 +89,7 @@ func (m Model) handleLogEntriesMsg(msg source.LogEntries) Model {
6989

7090
m.table.SetRows(msg.Rows())
7191
m.filteredLogEntries = msg
92+
m.table.UpdateViewport()
7293

7394
return m
7495
}

internal/app/property.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@ func (m Model) IsFilterShown() bool {
2424
func (m Model) IsFiltered() bool {
2525
return len(m.allLogEntries) != len(m.filteredLogEntries)
2626
}
27+
28+
// File returns the path to the current file.
29+
func (m Model) File() string {
30+
return m.fileLogPath
31+
}

internal/app/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (m Model) renderViews() string {
1818
}
1919

2020
if m.IsErrorShown() {
21-
return fmt.Sprintf("something went wrong: %s", m.err)
21+
return fmt.Sprintf("Something went wrong: %s.", m.err)
2222
}
2323

2424
var footer string

internal/pkg/source/source.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func LoadLogsFromFile(path string) func() tea.Msg {
1414
return func() (msg tea.Msg) {
1515
file, err := os.Open(path)
1616
if err != nil {
17-
return fmt.Errorf("opening: %w", err)
17+
return fmt.Errorf("os: %w", err)
1818
}
1919

2020
defer file.Close()

0 commit comments

Comments
 (0)