aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Kuehler <keur@ocf.berkeley.edu>2019-07-17 00:35:50 -0700
committerDrew DeVault <sir@cmpwn.com>2019-07-19 11:30:32 -0400
commitf81e4bd41c3ba9427390eadfc5133ed8daada6ab (patch)
tree1c815a23a33005dbd51bc0c4c1c1e1234fe696a5
parent8b2abcb02a191ad77c971fd4679c7d177ce2f827 (diff)
downloadaerc-f81e4bd41c3ba9427390eadfc5133ed8daada6ab.tar.gz
Implement :filter, :clear
Signed-off-by: Kevin Kuehler <keur@ocf.berkeley.edu>
-rw-r--r--commands/account/cf.go6
-rw-r--r--commands/account/clear.go34
-rw-r--r--commands/account/search.go30
-rw-r--r--config/binds.conf1
-rw-r--r--doc/aerc.1.scd26
-rw-r--r--lib/msgstore.go47
-rw-r--r--widgets/account.go2
-rw-r--r--widgets/msglist.go29
8 files changed, 134 insertions, 41 deletions
diff --git a/commands/account/cf.go b/commands/account/cf.go
index 6c928ead..2ebc2940 100644
--- a/commands/account/cf.go
+++ b/commands/account/cf.go
@@ -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
diff --git a/commands/account/clear.go b/commands/account/clear.go
new file mode 100644
index 00000000..bb9c04e4
--- /dev/null
+++ b/commands/account/clear.go
@@ -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
+}
diff --git a/commands/account/search.go b/commands/account/search.go
index 0687c5b3..da7ab03d 100644
--- a/commands/account/search.go
+++ b/commands/account/search.go
@@ -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")
}
- 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()
- })
+
+ 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...")
+ 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
}
diff --git a/config/binds.conf b/config/binds.conf
index b9b19be6..ac49bd0e 100644
--- a/config/binds.conf
+++ b/config/binds.conf
@@ -43,6 +43,7 @@ $ = :term<space>
| = :pipe<space>
/ = :search<space>
+\ = :filter<space>
n = :next-result<Enter>
N = :prev-result<Enter>
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index de82394d..4c5a552b 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -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).
diff --git a/lib/msgstore.go b/lib/msgstore.go
index 27b63f39..baf8ee46 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -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
}
}
diff --git a/widgets/account.go b/widgets/account.go
index 981a143a..f070df14 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -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
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 8968653a..e8ba8c1d 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -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 {