~sircmpwn/aerc

threading: Add threading support for maildir v1 PROPOSED

Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/aerc/patches/8667/mbox | git am -3
Learn more about email & git

[PATCH threading] Add threading support for maildir Export this patch

---
This is a (WIP) patch to help with implementing threading for the
maildir backend, to be applied on top of Kevin's patchset as discussed
on IRC.

Kevin, feel free to incorporate this into your patchset in any way that
would be helpful.

 worker/maildir/container.go | 46 +++++++++++++++++++++++++++++++++++++
 worker/maildir/message.go   | 17 ++++++++++++++
 worker/maildir/worker.go    | 15 ++++++++++++
 3 files changed, 78 insertions(+)

diff --git a/worker/maildir/container.go b/worker/maildir/container.go
index 85e892a..a7acfb7 100644
--- a/worker/maildir/container.go
+++ b/worker/maildir/container.go
@@ -10,6 +10,7 @@ import (
 	"github.com/emersion/go-maildir"
 
 	"git.sr.ht/~sircmpwn/aerc/lib/uidstore"
+	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
 // A Container is a directory which contains other directories which adhere to
@@ -123,3 +124,48 @@ func (c *Container) copyMessage(
 	_, err := src.Copy(dest, key)
 	return err
 }
+
+func (c *Container) ScanThreads(d maildir.Dir) ([]*types.Thread, error) {
+	uids, err := c.UIDs(d)
+	if err != nil {
+		return nil, err
+	}
+
+	// a map from message-id to the Thread that represents it
+	threads := make(map[string]*types.Thread)
+
+	for _, uid := range uids {
+		m, err := c.Message(d, uid)
+		if err != nil {
+			return nil, err
+		}
+		hs, err := m.Headers()
+		if err != nil {
+			return nil, err
+		}
+		msgID, err := hs.Text("message-id")
+		if err != nil {
+			return nil, err
+		}
+		irt, err := hs.Text("in-reply-to")
+		if err != nil {
+			return nil, err
+		}
+
+		t := &types.Thread{Uid: uid}
+		threads[msgID] = t
+
+		if p, ok := threads[irt]; ok {
+			p.Children = append(p.Children, t)
+			t.Parent = p
+		}
+	}
+
+	var threadSlice []*types.Thread
+	for _, t := range threads {
+		if t.Parent == nil {
+			threadSlice = append(threadSlice, t)
+		}
+	}
+	return threadSlice, nil
+}
diff --git a/worker/maildir/message.go b/worker/maildir/message.go
index dc5646b..283a3cd 100644
--- a/worker/maildir/message.go
+++ b/worker/maildir/message.go
@@ -1,6 +1,7 @@
 package maildir
 
 import (
+	"bufio"
 	"bytes"
 	"fmt"
 	"io"
@@ -8,6 +9,7 @@ import (
 
 	"github.com/emersion/go-maildir"
 	"github.com/emersion/go-message"
+	"github.com/emersion/go-message/textproto"
 
 	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker/lib"
@@ -119,3 +121,18 @@ func translateFlags(maildirFlags []maildir.Flag) []models.Flag {
 func (m Message) UID() uint32 {
 	return m.uid
 }
+
+func (m Message) Headers() (*message.Header, error) {
+	f, err := m.dir.Open(m.key)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	br := bufio.NewReader(f)
+	h, err := textproto.ReadHeader(br)
+	if err != nil {
+		return nil, err
+	}
+	mh := message.Header{h}
+	return &mh, nil
+}
diff --git a/worker/maildir/worker.go b/worker/maildir/worker.go
index 1df4e09..7bd7cbd 100644
--- a/worker/maildir/worker.go
+++ b/worker/maildir/worker.go
@@ -170,6 +170,8 @@ func (w *Worker) handleMessage(msg types.WorkerMessage) error {
 		return w.handleOpenDirectory(msg)
 	case *types.FetchDirectoryContents:
 		return w.handleFetchDirectoryContents(msg)
+	case *types.FetchDirectoryThreaded:
+		return w.handleDirectoryThreaded(msg)
 	case *types.CreateDirectory:
 		return w.handleCreateDirectory(msg)
 	case *types.FetchMessageHeaders:
@@ -291,6 +293,19 @@ func (w *Worker) handleFetchDirectoryContents(
 	return nil
 }
 
+func (w *Worker) handleDirectoryThreaded(msg *types.FetchDirectoryThreaded) error {
+	threads, err := w.c.ScanThreads(*w.selected)
+	if err != nil {
+		w.worker.Logger.Printf("error scanning threads: %v", err)
+		return err
+	}
+	w.worker.PostMessage(&types.DirectoryThreaded{
+		Message: types.RespondTo(msg),
+		Threads: threads,
+	}, nil)
+	return nil
+}
+
 func (w *Worker) sort(uids []uint32, criteria []*types.SortCriterion) ([]uint32, error) {
 	if len(criteria) == 0 {
 		return uids, nil
-- 
2.23.0
View this thread in the archives