Implement :filter, :clear
Signed-off-by: Kevin Kuehler <keur@ocf.berkeley.edu>
This commit is contained in:
parent
8b2abcb02a
commit
f81e4bd41c
8 changed files with 134 additions and 41 deletions
|
@ -34,14 +34,20 @@ func (_ ChangeFolder) Execute(aerc *widgets.Aerc, args []string) error {
|
|||
if acct == nil {
|
||||
return errors.New("No account selected")
|
||||
}
|
||||
store := acct.Store()
|
||||
if store == nil {
|
||||
return errors.New("Cannot perform action. Messages still loading")
|
||||
}
|
||||
previous := acct.Directories().Selected()
|
||||
if args[1] == "-" {
|
||||
if dir, ok := history[acct.Name()]; ok {
|
||||
store.ApplyClear()
|
||||
acct.Directories().Select(dir)
|
||||
} else {
|
||||
return errors.New("No previous folder to return to")
|
||||
}
|
||||
} else {
|
||||
store.ApplyClear()
|
||||
acct.Directories().Select(args[1])
|
||||
}
|
||||
history[acct.Name()] = previous
|
||||
|
|
34
commands/account/clear.go
Normal file
34
commands/account/clear.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.sr.ht/~sircmpwn/aerc/widgets"
|
||||
)
|
||||
|
||||
type Clear struct{}
|
||||
|
||||
func init() {
|
||||
register(Clear{})
|
||||
}
|
||||
|
||||
func (_ Clear) Aliases() []string {
|
||||
return []string{"clear"}
|
||||
}
|
||||
|
||||
func (_ Clear) Complete(aerc *widgets.Aerc, args []string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ Clear) Execute(aerc *widgets.Aerc, args []string) error {
|
||||
acct := aerc.SelectedAccount()
|
||||
if acct == nil {
|
||||
return errors.New("No account selected")
|
||||
}
|
||||
store := acct.Store()
|
||||
if store == nil {
|
||||
return errors.New("Cannot perform action. Messages still loading")
|
||||
}
|
||||
store.ApplyClear()
|
||||
aerc.SetStatus("Clear complete.")
|
||||
return nil
|
||||
}
|
|
@ -16,7 +16,7 @@ func init() {
|
|||
}
|
||||
|
||||
func (_ SearchFilter) Aliases() []string {
|
||||
return []string{"search"}
|
||||
return []string{"search", "filter"}
|
||||
}
|
||||
|
||||
func (_ SearchFilter) Complete(aerc *widgets.Aerc, args []string) []string {
|
||||
|
@ -54,13 +54,25 @@ func (_ SearchFilter) Execute(aerc *widgets.Aerc, args []string) error {
|
|||
if store == nil {
|
||||
return errors.New("Cannot perform action. Messages still loading")
|
||||
}
|
||||
|
||||
var cb func([]uint32)
|
||||
if args[0] == "filter" {
|
||||
aerc.SetStatus("Filtering...")
|
||||
cb = func(uids []uint32) {
|
||||
aerc.SetStatus("Filter complete.")
|
||||
acct.Logger().Printf("Filter results: %v", uids)
|
||||
store.ApplyFilter(uids)
|
||||
}
|
||||
} else {
|
||||
aerc.SetStatus("Searching...")
|
||||
store.Search(criteria, func(uids []uint32) {
|
||||
cb = func(uids []uint32) {
|
||||
aerc.SetStatus("Search complete.")
|
||||
acct.Logger().Printf("Search results: %v", uids)
|
||||
store.ApplySearch(uids)
|
||||
// TODO: Remove when stores have multiple OnUpdate handlers
|
||||
acct.Messages().Scroll()
|
||||
})
|
||||
}
|
||||
}
|
||||
store.Search(criteria, cb)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ $ = :term<space>
|
|||
| = :pipe<space>
|
||||
|
||||
/ = :search<space>
|
||||
\ = :filter<space>
|
||||
n = :next-result<Enter>
|
||||
N = :prev-result<Enter>
|
||||
|
||||
|
|
|
@ -114,6 +114,9 @@ message list, the message in the message viewer, etc).
|
|||
|
||||
## MESSAGE LIST COMMANDS
|
||||
|
||||
*clear*
|
||||
Clears the current search or filter criteria.
|
||||
|
||||
*cf* <folder>
|
||||
Change the folder shown in the message list.
|
||||
|
||||
|
@ -122,18 +125,33 @@ message list, the message in the message viewer, etc).
|
|||
the current account's outgoing transport configuration, see
|
||||
*aerc-config*(5) for details on configuring outgoing emails.
|
||||
|
||||
*filter* [options] <terms...>
|
||||
Similar to *search*, but filters the displayed messages to only the search
|
||||
results. See the documentation for *search* for more details.
|
||||
|
||||
*mkdir* <name>
|
||||
Creates a new folder for this account and changes to that folder.
|
||||
|
||||
*next-folder* <n>, *prev-folder* <n>
|
||||
Cycles to the next (or previous) folder shown in the sidebar, repeated n
|
||||
times (default: 1).
|
||||
|
||||
*next* <n>[%], *prev-message* <n>[%]
|
||||
Selects the next (or previous) message in the message list. If specified as
|
||||
a percentage, the percentage is applied to the number of messages shown on
|
||||
screen and the cursor advances that far.
|
||||
|
||||
*next-folder* <n>, *prev-folder* <n>
|
||||
Cycles to the next (or previous) folder shown in the sidebar, repeated n
|
||||
times (default: 1).
|
||||
|
||||
*next-result*, *prev-result*
|
||||
Selects the next or previous search result.
|
||||
|
||||
*search* [-ru] <terms...>
|
||||
Searches the current folder for <terms>. Each separate term is searched
|
||||
case-insensitively among subject lines.
|
||||
|
||||
*-r*: Search for read messages
|
||||
|
||||
*-u*: Search for unread messages
|
||||
|
||||
*select* <n>
|
||||
Selects the nth message in the message list (and scrolls it into view if
|
||||
necessary).
|
||||
|
|
|
@ -16,7 +16,7 @@ type MessageStore struct {
|
|||
DirInfo models.DirectoryInfo
|
||||
Messages map[uint32]*models.MessageInfo
|
||||
// Ordered list of known UIDs
|
||||
Uids []uint32
|
||||
uids []uint32
|
||||
|
||||
selected int
|
||||
bodyCallbacks map[uint32][]func(io.Reader)
|
||||
|
@ -25,6 +25,7 @@ type MessageStore struct {
|
|||
// Search/filter results
|
||||
results []uint32
|
||||
resultIndex int
|
||||
filter bool
|
||||
|
||||
// Map of uids we've asked the worker to fetch
|
||||
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
|
||||
|
@ -156,7 +157,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
|
|||
}
|
||||
}
|
||||
store.Messages = newMap
|
||||
store.Uids = msg.Uids
|
||||
store.uids = msg.Uids
|
||||
update = true
|
||||
case *types.MessageInfo:
|
||||
if existing, ok := store.Messages[msg.Info.Uid]; ok && existing != nil {
|
||||
|
@ -192,15 +193,15 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
|
|||
delete(store.Deleted, uid)
|
||||
}
|
||||
}
|
||||
uids := make([]uint32, len(store.Uids)-len(msg.Uids))
|
||||
uids := make([]uint32, len(store.uids)-len(msg.Uids))
|
||||
j := 0
|
||||
for _, uid := range store.Uids {
|
||||
for _, uid := range store.uids {
|
||||
if _, deleted := toDelete[uid]; !deleted && j < len(uids) {
|
||||
uids[j] = uid
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
store.Uids = uids
|
||||
store.uids = uids
|
||||
update = true
|
||||
}
|
||||
|
||||
|
@ -284,8 +285,15 @@ func (store *MessageStore) Read(uids []uint32, read bool,
|
|||
}, cb)
|
||||
}
|
||||
|
||||
func (store *MessageStore) Uids() []uint32 {
|
||||
if store.filter {
|
||||
return store.results
|
||||
}
|
||||
return store.uids
|
||||
}
|
||||
|
||||
func (store *MessageStore) Selected() *models.MessageInfo {
|
||||
return store.Messages[store.Uids[len(store.Uids)-store.selected-1]]
|
||||
return store.Messages[store.uids[len(store.uids)-store.selected-1]]
|
||||
}
|
||||
|
||||
func (store *MessageStore) SelectedIndex() int {
|
||||
|
@ -294,24 +302,24 @@ func (store *MessageStore) SelectedIndex() int {
|
|||
|
||||
func (store *MessageStore) Select(index int) {
|
||||
store.selected = index
|
||||
for ; store.selected < 0; store.selected = len(store.Uids) + store.selected {
|
||||
for ; store.selected < 0; store.selected = len(store.uids) + store.selected {
|
||||
/* This space deliberately left blank */
|
||||
}
|
||||
if store.selected > len(store.Uids) {
|
||||
store.selected = len(store.Uids)
|
||||
if store.selected > len(store.uids) {
|
||||
store.selected = len(store.uids)
|
||||
}
|
||||
}
|
||||
|
||||
func (store *MessageStore) nextPrev(delta int) {
|
||||
if len(store.Uids) == 0 {
|
||||
if len(store.uids) == 0 {
|
||||
return
|
||||
}
|
||||
store.selected += delta
|
||||
if store.selected < 0 {
|
||||
store.selected = 0
|
||||
}
|
||||
if store.selected >= len(store.Uids) {
|
||||
store.selected = len(store.Uids) - 1
|
||||
if store.selected >= len(store.uids) {
|
||||
store.selected = len(store.uids) - 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,6 +348,17 @@ func (store *MessageStore) ApplySearch(results []uint32) {
|
|||
store.NextResult()
|
||||
}
|
||||
|
||||
func (store *MessageStore) ApplyFilter(results []uint32) {
|
||||
store.results = results
|
||||
store.filter = true
|
||||
store.update()
|
||||
}
|
||||
|
||||
func (store *MessageStore) ApplyClear() {
|
||||
store.results = nil
|
||||
store.filter = false
|
||||
}
|
||||
|
||||
func (store *MessageStore) nextPrevResult(delta int) {
|
||||
if len(store.results) == 0 {
|
||||
return
|
||||
|
@ -351,9 +370,9 @@ func (store *MessageStore) nextPrevResult(delta int) {
|
|||
if store.resultIndex < 0 {
|
||||
store.resultIndex = len(store.results) - 1
|
||||
}
|
||||
for i, uid := range store.Uids {
|
||||
for i, uid := range store.uids {
|
||||
if store.results[len(store.results)-store.resultIndex-1] == uid {
|
||||
store.Select(len(store.Uids) - i - 1)
|
||||
store.Select(len(store.uids) - i - 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ func (acct *AccountView) SelectedAccount() *AccountView {
|
|||
}
|
||||
|
||||
func (acct *AccountView) SelectedMessage() (*models.MessageInfo, error) {
|
||||
if len(acct.msglist.Store().Uids) == 0 {
|
||||
if len(acct.msglist.Store().Uids()) == 0 {
|
||||
return nil, errors.New("no message selected")
|
||||
}
|
||||
return acct.msglist.Selected(), nil
|
||||
|
|
|
@ -56,9 +56,10 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
|
|||
needsHeaders []uint32
|
||||
row int = 0
|
||||
)
|
||||
uids := store.Uids()
|
||||
|
||||
for i := len(store.Uids) - 1 - ml.scroll; i >= 0; i-- {
|
||||
uid := store.Uids[i]
|
||||
for i := len(uids) - 1 - ml.scroll; i >= 0; i-- {
|
||||
uid := uids[i]
|
||||
msg := store.Messages[uid]
|
||||
|
||||
if row >= ctx.Height() {
|
||||
|
@ -106,7 +107,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
|
|||
row += 1
|
||||
}
|
||||
|
||||
if len(store.Uids) == 0 {
|
||||
if len(uids) == 0 {
|
||||
msg := ml.conf.Ui.EmptyMessage
|
||||
ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0,
|
||||
tcell.StyleDefault, "%s", msg)
|
||||
|
@ -128,23 +129,24 @@ func (ml *MessageList) storeUpdate(store *lib.MessageStore) {
|
|||
if ml.Store() != store {
|
||||
return
|
||||
}
|
||||
uids := store.Uids()
|
||||
|
||||
if len(store.Uids) > 0 {
|
||||
if len(uids) > 0 {
|
||||
// When new messages come in, advance the cursor accordingly
|
||||
// Note that this assumes new messages are appended to the top, which
|
||||
// isn't necessarily true once we implement SORT... ideally we'd look
|
||||
// for the previously selected UID.
|
||||
if len(store.Uids) > ml.nmsgs && ml.nmsgs != 0 {
|
||||
for i := 0; i < len(store.Uids)-ml.nmsgs; i++ {
|
||||
if len(uids) > ml.nmsgs && ml.nmsgs != 0 {
|
||||
for i := 0; i < len(uids)-ml.nmsgs; i++ {
|
||||
ml.Store().Next()
|
||||
}
|
||||
}
|
||||
if len(store.Uids) < ml.nmsgs && ml.nmsgs != 0 {
|
||||
for i := 0; i < ml.nmsgs-len(store.Uids); i++ {
|
||||
if len(uids) < ml.nmsgs && ml.nmsgs != 0 {
|
||||
for i := 0; i < ml.nmsgs-len(uids); i++ {
|
||||
ml.Store().Prev()
|
||||
}
|
||||
}
|
||||
ml.nmsgs = len(store.Uids)
|
||||
ml.nmsgs = len(uids)
|
||||
}
|
||||
|
||||
ml.Scroll()
|
||||
|
@ -158,7 +160,7 @@ func (ml *MessageList) SetStore(store *lib.MessageStore) {
|
|||
ml.store = store
|
||||
if store != nil {
|
||||
ml.spinner.Stop()
|
||||
ml.nmsgs = len(store.Uids)
|
||||
ml.nmsgs = len(store.Uids())
|
||||
store.OnUpdate(ml.storeUpdate)
|
||||
} else {
|
||||
ml.spinner.Start()
|
||||
|
@ -172,12 +174,13 @@ func (ml *MessageList) Store() *lib.MessageStore {
|
|||
|
||||
func (ml *MessageList) Empty() bool {
|
||||
store := ml.Store()
|
||||
return store == nil || len(store.Uids) == 0
|
||||
return store == nil || len(store.Uids()) == 0
|
||||
}
|
||||
|
||||
func (ml *MessageList) Selected() *models.MessageInfo {
|
||||
store := ml.Store()
|
||||
return store.Messages[store.Uids[len(store.Uids)-ml.store.SelectedIndex()-1]]
|
||||
uids := store.Uids()
|
||||
return store.Messages[uids[len(uids)-ml.store.SelectedIndex()-1]]
|
||||
}
|
||||
|
||||
func (ml *MessageList) Select(index int) {
|
||||
|
@ -189,7 +192,7 @@ func (ml *MessageList) Select(index int) {
|
|||
func (ml *MessageList) Scroll() {
|
||||
store := ml.Store()
|
||||
|
||||
if store == nil || len(store.Uids) == 0 {
|
||||
if store == nil || len(store.Uids()) == 0 {
|
||||
return
|
||||
}
|
||||
if ml.Height() != 0 {
|
||||
|
|
Loading…
Reference in a new issue