Implement :search, :next-result, :prev-result
This commit is contained in:
parent
ccf5c02c38
commit
91a75cd98b
5 changed files with 149 additions and 1 deletions
41
commands/account/next-result.go
Normal file
41
commands/account/next-result.go
Normal file
|
@ -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
|
||||
}
|
54
commands/account/search.go
Normal file
54
commands/account/search.go
Normal file
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue