Preserve cursor position after deleting entries and resizing

This commit is contained in:
David Dworken 2023-08-29 18:59:20 -07:00
parent 6bc70d0732
commit 23a377e3c1
No known key found for this signature in database
2 changed files with 23 additions and 8 deletions

View File

@ -1763,6 +1763,15 @@ func TestTui(t *testing.T) {
out = strings.TrimSpace(strings.Split(out, "hishtory tquery")[1]) out = strings.TrimSpace(strings.Split(out, "hishtory tquery")[1])
testutils.CompareGoldens(t, out, "TestTui-Resize") testutils.CompareGoldens(t, out, "TestTui-Resize")
// Check that the cursor position is maintained after it is resized
out = captureTerminalOutputWithShellNameAndDimensions(t, tester, tester.ShellName(), 100, 20, []TmuxCommand{
{Keys: "hishtory SPACE tquery ENTER"},
{Keys: "Down"},
{ResizeX: 300, ResizeY: 100},
{Keys: "Enter"},
})
require.Contains(t, out, "\necho 'aaaaaa bbbb'\n")
// Check that we can delete an entry // Check that we can delete an entry
out = captureTerminalOutput(t, tester, []string{ out = captureTerminalOutput(t, tester, []string{
"hishtory SPACE tquery ENTER", "hishtory SPACE tquery ENTER",

View File

@ -184,6 +184,7 @@ type asyncQueryFinishedMsg struct {
entries []*data.HistoryEntry entries []*data.HistoryEntry
searchErr error searchErr error
forceUpdateTable bool forceUpdateTable bool
maintainCursor 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 {
@ -205,7 +206,7 @@ func (m model) Init() tea.Cmd {
return m.spinner.Tick return m.spinner.Tick
} }
func updateTable(m model, forceUpdateTable bool, rows []table.Row, entries []*data.HistoryEntry, searchErr error) model { func updateTable(m model, rows []table.Row, entries []*data.HistoryEntry, searchErr error, forceUpdateTable, maintainCursor bool) model {
if m.runQuery == nil { if m.runQuery == nil {
m.runQuery = &m.lastQuery m.runQuery = &m.lastQuery
} }
@ -214,6 +215,7 @@ func updateTable(m model, forceUpdateTable bool, rows []table.Row, entries []*da
return m return m
} }
m.tableEntries = entries m.tableEntries = entries
initialCursor := m.table.Cursor()
if forceUpdateTable { if forceUpdateTable {
t, err := makeTable(m.ctx, rows) t, err := makeTable(m.ctx, rows)
if err != nil { if err != nil {
@ -223,7 +225,11 @@ func updateTable(m model, forceUpdateTable bool, rows []table.Row, entries []*da
m.table = t m.table = t
} }
m.table.SetRows(rows) m.table.SetRows(rows)
m.table.SetCursor(0) if maintainCursor {
m.table.SetCursor(initialCursor)
} else {
m.table.SetCursor(0)
}
m.lastQuery = *m.runQuery m.lastQuery = *m.runQuery
m.runQuery = nil m.runQuery = nil
if m.table.Cursor() >= len(m.tableEntries) { if m.table.Cursor() >= len(m.tableEntries) {
@ -240,7 +246,7 @@ func preventTableOverscrolling(m model) {
} }
} }
func runQueryAndUpdateTable(m model, forceUpdateTable bool) tea.Cmd { func runQueryAndUpdateTable(m model, forceUpdateTable, maintainCursor bool) tea.Cmd {
if (m.runQuery != nil && *m.runQuery != m.lastQuery) || forceUpdateTable || m.searchErr != nil { if (m.runQuery != nil && *m.runQuery != m.lastQuery) || forceUpdateTable || m.searchErr != nil {
query := m.lastQuery query := m.lastQuery
if m.runQuery != nil { if m.runQuery != nil {
@ -248,7 +254,7 @@ func runQueryAndUpdateTable(m model, forceUpdateTable bool) tea.Cmd {
} }
return func() tea.Msg { return func() tea.Msg {
rows, entries, searchErr := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, query, PADDED_NUM_ENTRIES) rows, entries, searchErr := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, query, PADDED_NUM_ENTRIES)
return asyncQueryFinishedMsg{rows, entries, searchErr, forceUpdateTable} return asyncQueryFinishedMsg{rows, entries, searchErr, forceUpdateTable, maintainCursor}
} }
} }
return nil return nil
@ -277,7 +283,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.fatalErr = err m.fatalErr = err
return m, nil return m, nil
} }
cmd := runQueryAndUpdateTable(m, true) cmd := runQueryAndUpdateTable(m, true, true)
preventTableOverscrolling(m) preventTableOverscrolling(m)
return m, cmd return m, cmd
case key.Matches(msg, keys.Help): case key.Matches(msg, keys.Help):
@ -293,13 +299,13 @@ 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
cmd3 := runQueryAndUpdateTable(m, false) cmd3 := runQueryAndUpdateTable(m, false, false)
preventTableOverscrolling(m) preventTableOverscrolling(m)
return m, tea.Batch(cmd1, cmd2, cmd3) return m, tea.Batch(cmd1, cmd2, cmd3)
} }
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.help.Width = msg.Width m.help.Width = msg.Width
cmd := runQueryAndUpdateTable(m, true) cmd := runQueryAndUpdateTable(m, true, true)
return m, cmd return m, cmd
case offlineMsg: case offlineMsg:
m.isOffline = true m.isOffline = true
@ -311,7 +317,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.isLoading = false m.isLoading = false
return m, nil return m, nil
case asyncQueryFinishedMsg: case asyncQueryFinishedMsg:
m = updateTable(m, msg.forceUpdateTable, msg.rows, msg.entries, msg.searchErr) m = updateTable(m, msg.rows, msg.entries, msg.searchErr, msg.forceUpdateTable, msg.maintainCursor)
return m, nil return m, nil
default: default:
var cmd tea.Cmd var cmd tea.Cmd