Make queries async for better TUI performance when sqlite is slow

This commit is contained in:
David Dworken 2023-08-27 11:42:17 -07:00
parent e9adb44f6b
commit a79d401058
No known key found for this signature in database
2 changed files with 58 additions and 27 deletions

View File

@ -179,6 +179,12 @@ type offlineMsg struct{}
type bannerMsg struct { type bannerMsg struct {
banner string banner string
} }
type asyncQueryFinishedMsg struct {
rows []table.Row
entries []*data.HistoryEntry
searchErr error
forceUpdateTable bool
}
func initialModel(ctx *context.Context, t table.Model, tableEntries []*data.HistoryEntry, initialQuery string) model { func initialModel(ctx *context.Context, t table.Model, tableEntries []*data.HistoryEntry, initialQuery string) model {
s := spinner.New() s := spinner.New()
@ -199,30 +205,27 @@ func (m model) Init() tea.Cmd {
return m.spinner.Tick return m.spinner.Tick
} }
func runQueryAndUpdateTable(m model, updateTable bool) model { func updateTable(m model, forceUpdateTable bool, rows []table.Row, entries []*data.HistoryEntry, searchErr error) model {
if (m.runQuery != nil && *m.runQuery != m.lastQuery) || updateTable || m.searchErr != nil { if m.runQuery == nil {
if m.runQuery == nil { m.runQuery = &m.lastQuery
m.runQuery = &m.lastQuery }
} m.searchErr = searchErr
rows, entries, err := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, *m.runQuery, PADDED_NUM_ENTRIES) if searchErr != nil {
m.searchErr = err return m
}
m.tableEntries = entries
if forceUpdateTable {
t, err := makeTable(m.ctx, rows)
if err != nil { if err != nil {
m.fatalErr = err
return m return m
} }
m.tableEntries = entries m.table = t
if updateTable {
t, err := makeTable(m.ctx, rows)
if err != nil {
m.fatalErr = err
return m
}
m.table = t
}
m.table.SetRows(rows)
m.table.SetCursor(0)
m.lastQuery = *m.runQuery
m.runQuery = nil
} }
m.table.SetRows(rows)
m.table.SetCursor(0)
m.lastQuery = *m.runQuery
m.runQuery = nil
if m.table.Cursor() >= len(m.tableEntries) { if m.table.Cursor() >= len(m.tableEntries) {
// Ensure that we can't scroll past the end of the table // Ensure that we can't scroll past the end of the table
m.table.SetCursor(len(m.tableEntries) - 1) m.table.SetCursor(len(m.tableEntries) - 1)
@ -230,6 +233,27 @@ func runQueryAndUpdateTable(m model, updateTable bool) model {
return m return m
} }
func preventTableOverscrolling(m model) {
if m.table.Cursor() >= len(m.tableEntries) {
// Ensure that we can't scroll past the end of the table
m.table.SetCursor(len(m.tableEntries) - 1)
}
}
func runQueryAndUpdateTable(m model, forceUpdateTable bool) tea.Cmd {
if (m.runQuery != nil && *m.runQuery != m.lastQuery) || forceUpdateTable || m.searchErr != nil {
query := m.lastQuery
if m.runQuery != nil {
query = *m.runQuery
}
return func() tea.Msg {
rows, entries, searchErr := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, query, PADDED_NUM_ENTRIES)
return asyncQueryFinishedMsg{rows, entries, searchErr, forceUpdateTable}
}
}
return nil
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
@ -253,8 +277,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.fatalErr = err m.fatalErr = err
return m, nil return m, nil
} }
m = runQueryAndUpdateTable(m, true) cmd := runQueryAndUpdateTable(m, true)
return m, nil preventTableOverscrolling(m)
return m, cmd
case key.Matches(msg, keys.Help): case key.Matches(msg, keys.Help):
m.help.ShowAll = !m.help.ShowAll m.help.ShowAll = !m.help.ShowAll
return m, nil return m, nil
@ -268,13 +293,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.queryInput = i m.queryInput = i
searchQuery := m.queryInput.Value() searchQuery := m.queryInput.Value()
m.runQuery = &searchQuery m.runQuery = &searchQuery
m = runQueryAndUpdateTable(m, false) cmd3 := runQueryAndUpdateTable(m, false)
return m, tea.Batch(cmd1, cmd2) preventTableOverscrolling(m)
return m, tea.Batch(cmd1, cmd2, cmd3)
} }
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.help.Width = msg.Width m.help.Width = msg.Width
m = runQueryAndUpdateTable(m, true) cmd := runQueryAndUpdateTable(m, true)
return m, nil return m, cmd
case offlineMsg: case offlineMsg:
m.isOffline = true m.isOffline = true
return m, nil return m, nil
@ -284,6 +310,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case doneDownloadingMsg: case doneDownloadingMsg:
m.isLoading = false m.isLoading = false
return m, nil return m, nil
case asyncQueryFinishedMsg:
m = updateTable(m, msg.forceUpdateTable, msg.rows, msg.entries, msg.searchErr)
return m, nil
default: default:
var cmd tea.Cmd var cmd tea.Cmd
if m.isLoading { if m.isLoading {
@ -592,3 +621,5 @@ func TuiQuery(ctx *context.Context, initialQuery string) error {
// TODO: support custom key bindings // TODO: support custom key bindings
// TODO: make the help page wrap // TODO: make the help page wrap
// TODO: FR: when updating the search query, try to maintain position in the table
// TODO: FR: when deleting an entry, try to maintain position in the table

View File

@ -360,7 +360,7 @@ func CompareGoldens(t *testing.T, out, goldenName string) {
} }
func normalizeHostnames(data string) string { func normalizeHostnames(data string) string {
hostnames := []string{"Davids-MacBook-Air.local", "ghaction-runner-hostname"} hostnames := []string{"Davids-MacBook-Air.local", "ghaction-runner-hostname", "Davids-Air"}
for _, hostname := range hostnames { for _, hostname := range hostnames {
data = strings.ReplaceAll(data, hostname, "ghaction-runner-hostname") data = strings.ReplaceAll(data, hostname, "ghaction-runner-hostname")
} }