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>
|
! = :term<space>
|
||||||
| = :pipe<space>
|
| = :pipe<space>
|
||||||
|
|
||||||
|
/ = :search<space>
|
||||||
|
n = :next-result<Enter>
|
||||||
|
N = :prev-result<Enter>
|
||||||
|
|
||||||
[view]
|
[view]
|
||||||
q = :close<Enter>
|
q = :close<Enter>
|
||||||
| = :pipe<space>
|
| = :pipe<space>
|
||||||
|
|
|
@ -21,6 +21,10 @@ type MessageStore struct {
|
||||||
bodyCallbacks map[uint32][]func(io.Reader)
|
bodyCallbacks map[uint32][]func(io.Reader)
|
||||||
headerCallbacks map[uint32][]func(*types.MessageInfo)
|
headerCallbacks map[uint32][]func(*types.MessageInfo)
|
||||||
|
|
||||||
|
// Search/filter results
|
||||||
|
results []uint32
|
||||||
|
resultIndex int
|
||||||
|
|
||||||
// Map of uids we've asked the worker to fetch
|
// Map of uids we've asked the worker to fetch
|
||||||
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
|
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
|
||||||
pendingBodies map[uint32]interface{}
|
pendingBodies map[uint32]interface{}
|
||||||
|
@ -107,7 +111,6 @@ func (store *MessageStore) FetchBodyPart(
|
||||||
}
|
}
|
||||||
|
|
||||||
func merge(to *types.MessageInfo, from *types.MessageInfo) {
|
func merge(to *types.MessageInfo, from *types.MessageInfo) {
|
||||||
|
|
||||||
if from.BodyStructure != nil {
|
if from.BodyStructure != nil {
|
||||||
to.BodyStructure = from.BodyStructure
|
to.BodyStructure = from.BodyStructure
|
||||||
}
|
}
|
||||||
|
@ -320,3 +323,48 @@ func (store *MessageStore) Next() {
|
||||||
func (store *MessageStore) Prev() {
|
func (store *MessageStore) Prev() {
|
||||||
store.nextPrev(-1)
|
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.nmsgs = len(store.Uids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ml.Scroll()
|
||||||
ml.Invalidate()
|
ml.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue