diff options
| author | Drew DeVault <sir@cmpwn.com> | 2019-06-26 20:50:27 -0400 | 
|---|---|---|
| committer | Drew DeVault <sir@cmpwn.com> | 2019-06-26 20:50:54 -0400 | 
| commit | 91a75cd98b705bd5e6689b154ecaca0e7c81630e (patch) | |
| tree | 85fd35605953bad88f9ad2b3a5875f5490b59f09 | |
| parent | ccf5c02c3815efbe3b2e495cbc6eaca9f017aefd (diff) | |
Implement :search, :next-result, :prev-result
| -rw-r--r-- | commands/account/next-result.go | 41 | ||||
| -rw-r--r-- | commands/account/search.go | 54 | ||||
| -rw-r--r-- | config/binds.conf | 4 | ||||
| -rw-r--r-- | lib/msgstore.go | 50 | ||||
| -rw-r--r-- | widgets/msglist.go | 1 | 
5 files changed, 149 insertions, 1 deletions
diff --git a/commands/account/next-result.go b/commands/account/next-result.go new file mode 100644 index 0000000..d89de56 --- /dev/null +++ b/commands/account/next-result.go @@ -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 +} diff --git a/commands/account/search.go b/commands/account/search.go new file mode 100644 index 0000000..513ad43 --- /dev/null +++ b/commands/account/search.go @@ -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 +} diff --git a/config/binds.conf b/config/binds.conf index 8c4af95..75b02b3 100644 --- a/config/binds.conf +++ b/config/binds.conf @@ -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> diff --git a/lib/msgstore.go b/lib/msgstore.go index a81f9ad..45a9fb6 100644 --- a/lib/msgstore.go +++ b/lib/msgstore.go @@ -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) +} diff --git a/widgets/msglist.go b/widgets/msglist.go index ae50b0d..3f7e2b3 100644 --- a/widgets/msglist.go +++ b/widgets/msglist.go @@ -148,6 +148,7 @@ func (ml *MessageList) storeUpdate(store *lib.MessageStore) {  		ml.nmsgs = len(store.Uids)  	} +	ml.Scroll()  	ml.Invalidate()  }  | 
