summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2022-07-26 11:30:26 +0200
committerRobin Jarry <robin@jarry.cc>2022-07-26 11:34:19 +0200
commit8f7695fde5cd84b7f6b8f3193270eda2fd62448c (patch)
tree27d2b58969b841f957fdbb8ad703f3d116eb5f9c
parent3b90b3b0ddfd8134daa1d417bdd7acd8c39781b7 (diff)
downloadaerc-8f7695fde5cd84b7f6b8f3193270eda2fd62448c.zip
msgstore: implement a uid-based architecture
Change the message store architecture from an index-based to a uid-based one. Key advantage of this design approach is that no reselect mechanism is required anymore since it comes with the design for free. Fixes: https://todo.sr.ht/~rjarry/aerc/43 Signed-off-by: Koni Marti <koni.marti@gmail.com> Tested-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--commands/account/clear.go2
-rw-r--r--commands/msg/delete.go4
-rw-r--r--commands/msg/toggle-threads.go1
-rw-r--r--lib/msgstore.go127
-rw-r--r--widgets/msglist.go25
5 files changed, 73 insertions, 86 deletions
diff --git a/commands/account/clear.go b/commands/account/clear.go
index 5bab710..af7da32 100644
--- a/commands/account/clear.go
+++ b/commands/account/clear.go
@@ -51,8 +51,6 @@ func (Clear) Execute(aerc *widgets.Aerc, args []string) error {
if clearSelected {
defer store.Select(0)
- } else {
- store.SetReselect(store.Selected())
}
store.ApplyClear()
acct.SetStatus(statusline.SearchFilterClear())
diff --git a/commands/msg/delete.go b/commands/msg/delete.go
index 2d7ad0f..d144388 100644
--- a/commands/msg/delete.go
+++ b/commands/msg/delete.go
@@ -56,7 +56,7 @@ func (Delete) Execute(aerc *widgets.Aerc, args []string) error {
// no more messages in the list
if next == nil {
aerc.RemoveTab(h.msgProvider)
- store.Select(len(store.Uids()))
+ acct.Messages().Select(0)
acct.Messages().Invalidate()
return
}
@@ -74,7 +74,7 @@ func (Delete) Execute(aerc *widgets.Aerc, args []string) error {
if next == nil {
// We deleted the last message, select the new last message
// instead of the first message
- store.Select(len(store.Uids()))
+ acct.Messages().Select(0)
}
}
acct.Messages().Invalidate()
diff --git a/commands/msg/toggle-threads.go b/commands/msg/toggle-threads.go
index 05c2c5e..af694bc 100644
--- a/commands/msg/toggle-threads.go
+++ b/commands/msg/toggle-threads.go
@@ -34,7 +34,6 @@ func (ToggleThreads) Execute(aerc *widgets.Aerc, args []string) error {
if err != nil {
return err
}
- store.SetReselect(store.Selected())
store.SetThreadedView(!store.ThreadedView())
acct.SetStatus(statusline.Threading(store.ThreadedView()))
acct.Messages().Invalidate()
diff --git a/lib/msgstore.go b/lib/msgstore.go
index 34c2676..61a6dd4 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -20,7 +20,7 @@ type MessageStore struct {
uids []uint32
Threads []*types.Thread
- selected int
+ selectedUid uint32
reselect *models.MessageInfo
bodyCallbacks map[uint32][]func(*types.FullMessage)
headerCallbacks map[uint32][]func(*types.MessageInfo)
@@ -57,6 +57,8 @@ type MessageStore struct {
dirInfoUpdateDelay time.Duration
}
+const MagicUid = 0xFFFFFFFF
+
func NewMessageStore(worker *types.Worker,
dirInfo *models.DirectoryInfo,
defaultSortCriteria []*types.SortCriterion,
@@ -75,7 +77,7 @@ func NewMessageStore(worker *types.Worker,
DirInfo: *dirInfo,
Messages: make(map[uint32]*models.MessageInfo),
- selected: 0,
+ selectedUid: MagicUid,
marked: make(map[uint32]struct{}),
bodyCallbacks: make(map[uint32][]func(*types.FullMessage)),
headerCallbacks: make(map[uint32][]func(*types.MessageInfo)),
@@ -197,9 +199,6 @@ func merge(to *models.MessageInfo, from *models.MessageInfo) {
func (store *MessageStore) Update(msg types.WorkerMessage) {
update := false
directoryChange := false
- if store.reselect == nil {
- store.SetReselect(store.Selected())
- }
switch msg := msg.(type) {
case *types.DirectoryInfo:
store.DirInfo = *msg.Info
@@ -367,7 +366,6 @@ func (store *MessageStore) SetThreadedView(thread bool) {
if store.threadedView {
store.runThreadBuilder()
}
- store.Reselect()
return
}
store.Sort(store.sortCriteria, nil)
@@ -490,54 +488,20 @@ func (store *MessageStore) Uids() []uint32 {
}
func (store *MessageStore) Selected() *models.MessageInfo {
- uids := store.Uids()
- idx := len(uids) - store.selected - 1
- if len(uids) == 0 || idx < 0 || idx >= len(uids) {
- return nil
- }
- return store.Messages[uids[idx]]
-}
-
-func (store *MessageStore) SelectedIndex() int {
- return store.selected
+ return store.Messages[store.selectedUid]
}
-func (store *MessageStore) Select(index int) {
- l := len(store.Uids())
- switch {
- case l+index < 0:
- // negative index overruns length of list
- store.selected = 0
- case index < 0:
- // negative index, select from bottom
- store.selected = l + index
- case index >= l:
- // index greater than length, select last
- store.selected = l - 1
- default:
- store.selected = index
+func (store *MessageStore) SelectedUid() uint32 {
+ if store.selectedUid == MagicUid && len(store.Uids()) > 0 {
+ uids := store.Uids()
+ store.selectedUid = uids[len(uids)-1]
}
- store.updateVisual()
+ return store.selectedUid
}
-func (store *MessageStore) Reselect() {
- if store.reselect == nil {
- return
- }
- uid := store.reselect.Uid
- newIdx := 0
- for idx, uidStore := range store.Uids() {
- if uidStore == uid {
- newIdx = len(store.Uids()) - idx - 1
- break
- }
- }
- store.reselect = nil
- store.Select(newIdx)
-}
-
-func (store *MessageStore) SetReselect(info *models.MessageInfo) {
- store.reselect = info
+func (store *MessageStore) Select(uid uint32) {
+ store.selectedUid = uid
+ store.updateVisual()
}
// Mark sets the marked state on a MessageInfo
@@ -648,15 +612,20 @@ func (store *MessageStore) updateVisual() {
store.ClearVisualMark()
return
}
- uidLen := len(store.Uids())
- // store.selected is the inverted form of the actual array
- selectedIdx := uidLen - store.selected - 1
+
+ selectedIdx := store.FindIndexByUid(store.SelectedUid())
+ if selectedIdx < 0 {
+ store.ClearVisualMark()
+ return
+ }
+
var visUids []uint32
if selectedIdx > startIdx {
visUids = store.Uids()[startIdx : selectedIdx+1]
} else {
visUids = store.Uids()[selectedIdx : startIdx+1]
}
+
store.resetMark()
for _, uid := range visUids {
store.marked[uid] = struct{}{}
@@ -675,20 +644,31 @@ func (store *MessageStore) NextPrev(delta int) {
if len(uids) == 0 {
return
}
- idx := store.SelectedIndex() + delta
- if idx < 0 {
- store.Select(0)
- } else {
- store.Select(idx)
+
+ uid := store.SelectedUid()
+
+ newIdx := store.FindIndexByUid(uid)
+ if newIdx < 0 {
+ store.Select(uids[len(uids)-1])
+ }
+
+ newIdx -= delta
+ if newIdx >= len(uids) {
+ newIdx = len(uids) - 1
+ } else if newIdx < 0 {
+ newIdx = 0
}
+
+ store.Select(uids[newIdx])
+
store.updateVisual()
+
nextResultIndex := len(store.results) - store.resultIndex - 2*delta
if nextResultIndex < 0 || nextResultIndex >= len(store.results) {
return
}
nextResultUid := store.results[nextResultIndex]
- selectedUid := uids[len(uids)-store.selected-1]
- if nextResultUid == selectedUid {
+ if nextResultUid == store.SelectedUid() {
store.resultIndex += delta
}
}
@@ -734,21 +714,12 @@ func (store *MessageStore) SetFilter(args []string) {
}
func (store *MessageStore) ApplyClear() {
- if store.reselect == nil {
- store.SetReselect(store.Selected())
- }
store.filter = []string{"filter"}
store.results = nil
if store.onFilterChange != nil {
store.onFilterChange(store)
}
- cb := func(msg types.WorkerMessage) {
- switch msg.(type) {
- case *types.Done:
- store.Reselect()
- }
- }
- store.Sort(nil, cb)
+ store.Sort(nil, nil)
}
func (store *MessageStore) nextPrevResult(delta int) {
@@ -762,13 +733,7 @@ func (store *MessageStore) nextPrevResult(delta int) {
if store.resultIndex < 0 {
store.resultIndex = len(store.results) - 1
}
- uids := store.Uids()
- for i, uid := range uids {
- if store.results[len(store.results)-store.resultIndex-1] == uid {
- store.Select(len(uids) - i - 1)
- break
- }
- }
+ store.Select(store.results[len(store.results)-store.resultIndex-1])
store.update()
}
@@ -826,3 +791,13 @@ func (store *MessageStore) visualStartIdx() int {
}
return -1
}
+
+// FindIndexByUid returns the index in store.Uids() or -1 if not found
+func (store *MessageStore) FindIndexByUid(uid uint32) int {
+ for idx, u := range store.Uids() {
+ if u == uid {
+ return idx
+ }
+ }
+ return -1
+}
diff --git a/widgets/msglist.go b/widgets/msglist.go
index a190c18..4c7c1c2 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -69,7 +69,9 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
ml.UpdateScroller(ml.height, len(store.Uids()))
if store := ml.Store(); store != nil && len(store.Uids()) > 0 {
- ml.EnsureScroll(store.SelectedIndex())
+ if idx := store.FindIndexByUid(store.SelectedUid()); idx >= 0 {
+ ml.EnsureScroll(len(store.Uids()) - idx - 1)
+ }
}
textWidth := ctx.Width()
@@ -244,7 +246,7 @@ func (ml *MessageList) drawRow(textWidth int, ctx *ui.Context, uid uint32, row i
var style tcell.Style
// current row
- if row == ml.store.SelectedIndex()-ml.Scroll() {
+ if msg.Uid == ml.store.SelectedUid() {
style = uiConfig.GetComposedStyleSelected(config.STYLE_MSGLIST_DEFAULT, msg_styles)
} else {
style = uiConfig.GetComposedStyle(config.STYLE_MSGLIST_DEFAULT, msg_styles)
@@ -342,7 +344,6 @@ func (ml *MessageList) storeUpdate(store *lib.MessageStore) {
if ml.Store() != store {
return
}
- store.Reselect()
}
func (ml *MessageList) SetStore(store *lib.MessageStore) {
@@ -352,7 +353,8 @@ func (ml *MessageList) SetStore(store *lib.MessageStore) {
ml.store = store
if store != nil {
ml.spinner.Stop()
- ml.nmsgs = len(store.Uids())
+ uids := store.Uids()
+ ml.nmsgs = len(uids)
store.OnUpdate(ml.storeUpdate)
store.OnFilterChange(func(store *lib.MessageStore) {
if ml.Store() != store {
@@ -384,8 +386,21 @@ func (ml *MessageList) Selected() *models.MessageInfo {
}
func (ml *MessageList) Select(index int) {
+ // Note that the msgstore.Select function expects a uid as argument
+ // whereas the msglist.Select expects the message number
store := ml.Store()
- store.Select(index)
+ uids := store.Uids()
+ if len(uids) == 0 {
+ return
+ }
+ uidIdx := len(uids) - index - 1
+ if uidIdx >= len(store.Uids()) {
+ uidIdx = 0
+ } else if uidIdx < 0 {
+ uidIdx = len(store.Uids()) - 1
+ }
+ store.Select(store.Uids()[uidIdx])
+
ml.Invalidate()
}