1
0
mirror of https://git.sr.ht/~sircmpwn/aerc synced 2024-11-23 16:42:07 +01:00

Implement :search, :next-result, :prev-result

This commit is contained in:
Drew DeVault 2019-06-26 20:50:27 -04:00
parent ccf5c02c38
commit 91a75cd98b
5 changed files with 149 additions and 1 deletions

@ -0,0 +1,41 @@
package account
import (
"errors"
"fmt"
"git.sr.ht/~sircmpwn/aerc/widgets"
)
func init() {
register("next-result", NextPrevResult)
register("prev-result", NextPrevResult)
}
func nextPrevResultUsage(cmd string) error {
return errors.New(fmt.Sprintf("Usage: %s [<n>[%%]]", cmd))
}
func NextPrevResult(aerc *widgets.Aerc, args []string) error {
if len(args) > 1 {
return nextPrevResultUsage(args[0])
}
acct := aerc.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
if args[0] == "prev-result" {
store := acct.Store()
if store != nil {
store.PrevResult()
}
acct.Messages().Scroll()
} else {
store := acct.Store()
if store != nil {
store.NextResult()
}
acct.Messages().Scroll()
}
return nil
}

@ -0,0 +1,54 @@
package account
import (
"errors"
"git.sr.ht/~sircmpwn/getopt"
"github.com/emersion/go-imap"
"git.sr.ht/~sircmpwn/aerc/widgets"
)
func init() {
register("search", SearchFilter)
//register("filter", SearchFilter) // TODO
}
func SearchFilter(aerc *widgets.Aerc, args []string) error {
var (
criteria *imap.SearchCriteria = imap.NewSearchCriteria()
)
opts, optind, err := getopt.Getopts(args, "ruH:")
if err != nil {
return err
}
for _, opt := range opts {
switch opt.Option {
case 'r':
criteria.WithFlags = append(criteria.WithFlags, imap.SeenFlag)
case 'u':
criteria.WithoutFlags = append(criteria.WithoutFlags, imap.SeenFlag)
case 'H':
// TODO
}
}
for _, arg := range args[optind:] {
criteria.Header.Add("Subject", arg)
}
acct := aerc.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
store := acct.Store()
aerc.SetStatus("Searching...")
store.Search(criteria, 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()
})
return nil
}

@ -42,6 +42,10 @@ $ = :term<space>
! = :term<space>
| = :pipe<space>
/ = :search<space>
n = :next-result<Enter>
N = :prev-result<Enter>
[view]
q = :close<Enter>
| = :pipe<space>

@ -21,6 +21,10 @@ type MessageStore struct {
bodyCallbacks map[uint32][]func(io.Reader)
headerCallbacks map[uint32][]func(*types.MessageInfo)
// Search/filter results
results []uint32
resultIndex int
// Map of uids we've asked the worker to fetch
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
pendingBodies map[uint32]interface{}
@ -107,7 +111,6 @@ func (store *MessageStore) FetchBodyPart(
}
func merge(to *types.MessageInfo, from *types.MessageInfo) {
if from.BodyStructure != nil {
to.BodyStructure = from.BodyStructure
}
@ -320,3 +323,48 @@ func (store *MessageStore) Next() {
func (store *MessageStore) Prev() {
store.nextPrev(-1)
}
func (store *MessageStore) Search(c *imap.SearchCriteria, cb func([]uint32)) {
store.worker.PostAction(&types.SearchDirectory{
Criteria: c,
}, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.SearchResults:
cb(msg.Uids)
}
})
}
func (store *MessageStore) ApplySearch(results []uint32) {
store.results = results
store.resultIndex = -1
store.NextResult()
}
func (store *MessageStore) nextPrevResult(delta int) {
if len(store.results) == 0 {
return
}
store.resultIndex += delta
if store.resultIndex >= len(store.results) {
store.resultIndex = 0
}
if store.resultIndex < 0 {
store.resultIndex = len(store.results) - 1
}
for i, uid := range store.Uids {
if store.results[len(store.results)-store.resultIndex-1] == uid {
store.Select(len(store.Uids) - i - 1)
break
}
}
store.update()
}
func (store *MessageStore) NextResult() {
store.nextPrevResult(1)
}
func (store *MessageStore) PrevResult() {
store.nextPrevResult(-1)
}

@ -148,6 +148,7 @@ func (ml *MessageList) storeUpdate(store *lib.MessageStore) {
ml.nmsgs = len(store.Uids)
}
ml.Scroll()
ml.Invalidate()
}