aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2023-07-18 10:29:52 +0200
committerRobin Jarry <robin@jarry.cc>2024-06-25 15:28:11 +0200
commit7f66297c521fca8f9bc17280f0a96874598bde96 (patch)
tree643e1d5c6dbbf55814c689f2509f3eb9bd532fa2
parent9cd806fa6e80829753ecd3356e19044d6e210826 (diff)
downloadaerc-7f66297c521fca8f9bc17280f0a96874598bde96.tar.gz
threadbuilder: show siblings even when no parent found
Show all threading associations even when not all nodes are present. Indicate if a thread is incomplete, i.e. misses a direct parent node. Use the `msglist_thread_orphan.fg=red` styleobject in your stylesheet to indicate whether a messsage has a missing parent. Also use a different thread prefix ("┬─" instead of "├─") not to confuse them with regular threads that have a visible parent. Signed-off-by: Koni Marti <koni.marti@gmail.com> Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Matěj Cepl <mcepl@cepl.eu>
-rw-r--r--app/msglist.go30
-rw-r--r--config/style.go2
-rw-r--r--config/templates.go1
-rw-r--r--doc/aerc-stylesets.7.scd3
-rw-r--r--lib/state/templates.go18
-rw-r--r--lib/threadbuilder.go36
-rw-r--r--models/templates.go1
-rw-r--r--worker/types/thread.go3
8 files changed, 62 insertions, 32 deletions
diff --git a/app/msglist.go b/app/msglist.go
index 996ffa0c..24547b64 100644
--- a/app/msglist.go
+++ b/app/msglist.go
@@ -231,6 +231,9 @@ func addMessage(
if templateData.ThreadContext() {
params.styles = append(params.styles, config.STYLE_MSGLIST_THREAD_CONTEXT)
}
+ if templateData.ThreadOrphan() {
+ params.styles = append(params.styles, config.STYLE_MSGLIST_THREAD_ORPHAN)
+ }
}
// marked message
marked := store.Marker().IsMarked(msg.Uid)
@@ -482,16 +485,20 @@ func threadPrefix(t *types.Thread, reverse bool, msglist bool) string {
var hiddenOffspring bool = t.FirstChild != nil && t.FirstChild.Hidden > 0
var parentAndSiblings bool = t.Parent != nil && t.NextSibling != nil
+ var hasSiblings string = uiConfig.ThreadPrefixHasSiblings
+ if t.Parent != nil && t.Parent.Hidden > 0 && t.Hidden == 0 {
+ hasSiblings = dummy
+ }
switch {
case parentAndSiblings && hiddenOffspring:
- prefix = uiConfig.ThreadPrefixHasSiblings +
+ prefix = hasSiblings +
uiConfig.ThreadPrefixFolded
case parentAndSiblings && t.FirstChild != nil:
- prefix = uiConfig.ThreadPrefixHasSiblings +
+ prefix = hasSiblings +
firstChild + tip
case parentAndSiblings:
- prefix = uiConfig.ThreadPrefixHasSiblings +
+ prefix = hasSiblings +
uiConfig.ThreadPrefixLimb +
uiConfig.ThreadPrefixUnfolded + tip
case t.Parent != nil && hiddenOffspring:
@@ -557,18 +564,19 @@ func newThreadView(store *lib.MessageStore) *threadView {
}
func (t *threadView) Update(data state.DataSetter, uid uint32) {
- prefix, same, count, unread, folded, context := "", false, 0, 0, false, false
thread, err := t.store.Thread(uid)
+ info := state.ThreadInfo{}
if thread != nil && err == nil {
- prefix = threadPrefix(thread, t.reverse, true)
+ info.Prefix = threadPrefix(thread, t.reverse, true)
subject := threadSubject(t.store, thread)
- same = subject == t.prevSubj && sameParent(thread, t.prev) && !isParent(thread)
+ info.SameSubject = subject == t.prevSubj && sameParent(thread, t.prev) && !isParent(thread)
t.prev = thread
t.prevSubj = subject
- count = countThreads(thread)
- unread = unreadInThread(thread, t.store)
- folded = thread.FirstChild != nil && thread.FirstChild.Hidden != 0
- context = thread.Context
+ info.Count = countThreads(thread)
+ info.Unread = unreadInThread(thread, t.store)
+ info.Folded = thread.FirstChild != nil && thread.FirstChild.Hidden != 0
+ info.Context = thread.Context
+ info.Orphan = thread.Parent != nil && thread.Parent.Hidden > 0 && thread.Hidden == 0
}
- data.SetThreading(prefix, same, count, unread, folded, context)
+ data.SetThreading(info)
}
diff --git a/config/style.go b/config/style.go
index 95e60616..46c46378 100644
--- a/config/style.go
+++ b/config/style.go
@@ -42,6 +42,7 @@ const (
STYLE_MSGLIST_GUTTER
STYLE_MSGLIST_PILL
STYLE_MSGLIST_THREAD_CONTEXT
+ STYLE_MSGLIST_THREAD_ORPHAN
STYLE_DIRLIST_DEFAULT
STYLE_DIRLIST_UNREAD
@@ -92,6 +93,7 @@ var StyleNames = map[string]StyleObject{
"msglist_thread_folded": STYLE_MSGLIST_THREAD_FOLDED,
"msglist_thread_context": STYLE_MSGLIST_THREAD_CONTEXT,
+ "msglist_thread_orphan": STYLE_MSGLIST_THREAD_ORPHAN,
"dirlist_default": STYLE_DIRLIST_DEFAULT,
"dirlist_unread": STYLE_DIRLIST_UNREAD,
diff --git a/config/templates.go b/config/templates.go
index 5a38cd61..4000b1fc 100644
--- a/config/templates.go
+++ b/config/templates.go
@@ -82,6 +82,7 @@ func (d *dummyData) ThreadCount() int { return 0 }
func (d *dummyData) ThreadUnread() int { return 0 }
func (d *dummyData) ThreadFolded() bool { return false }
func (d *dummyData) ThreadContext() bool { return true }
+func (d *dummyData) ThreadOrphan() bool { return true }
func (d *dummyData) Subject() string { return "Re: [PATCH] hey" }
func (d *dummyData) SubjectBase() string { return "[PATCH] hey" }
func (d *dummyData) Attach(string) string { return "" }
diff --git a/doc/aerc-stylesets.7.scd b/doc/aerc-stylesets.7.scd
index bfe9dc05..d86d9f98 100644
--- a/doc/aerc-stylesets.7.scd
+++ b/doc/aerc-stylesets.7.scd
@@ -117,6 +117,8 @@ styling.
: Visible messages that have folded thread children.
| *msglist_thread_context*
: The messages not matching the mailbox / query, displayed for context.
+| *msglist_thread_orphan*
+: Threaded messages that have a missing parent message.
| *dirlist_default*
: The default style for directories in the directory list.
| *dirlist_unread*
@@ -287,6 +289,7 @@ The order that *msglist_\** styles are applied in is, from first to last:
. *msglist_result*
. *msglist_thread_folded*
. *msglist_thread_context*
+. *msglist_thread_orphan*
. *msglist_marked*
So, the marked style will override all other msglist styles.
diff --git a/lib/state/templates.go b/lib/state/templates.go
index e5206cf0..868f9184 100644
--- a/lib/state/templates.go
+++ b/lib/state/templates.go
@@ -28,7 +28,7 @@ type DataSetter interface {
SetHeaders(*mail.Header, *models.OriginalMail)
SetInfo(*models.MessageInfo, int, bool)
SetVisual(bool)
- SetThreading(string, bool, int, int, bool, bool)
+ SetThreading(ThreadInfo)
SetComposer(Composer)
SetAccount(*config.AccountConfig)
SetFolder(*models.Directory)
@@ -44,6 +44,7 @@ type ThreadInfo struct {
Unread int
Folded bool
Context bool
+ Orphan bool
}
type templateData struct {
@@ -100,15 +101,8 @@ func (d *templateData) SetVisual(visual bool) {
d.visual = visual
}
-func (d *templateData) SetThreading(prefix string, same bool, count int,
- unread int, folded bool, context bool,
-) {
- d.threadInfo.Prefix = prefix
- d.threadInfo.SameSubject = same
- d.threadInfo.Count = count
- d.threadInfo.Unread = unread
- d.threadInfo.Folded = folded
- d.threadInfo.Context = context
+func (d *templateData) SetThreading(info ThreadInfo) {
+ d.threadInfo = info
}
func (d *templateData) SetAccount(acct *config.AccountConfig) {
@@ -334,6 +328,10 @@ func (d *templateData) ThreadContext() bool {
return d.threadInfo.Context
}
+func (d *templateData) ThreadOrphan() bool {
+ return d.threadInfo.Orphan
+}
+
func (d *templateData) Subject() string {
var subject string
switch {
diff --git a/lib/threadbuilder.go b/lib/threadbuilder.go
index c18ab3d0..b7dae431 100644
--- a/lib/threadbuilder.go
+++ b/lib/threadbuilder.go
@@ -165,23 +165,30 @@ func (builder *ThreadBuilder) buildTree(c jwz.Threadable, parent *types.Thread,
return
}
for node := c; node != nil; node = node.GetNext() {
- thread := parent
- if !node.IsDummy() {
- thread = builder.newThread(node, parent)
- if rootLevel {
- thread.NextSibling = parent.FirstChild
- parent.FirstChild = thread
- } else {
- parent.InsertCmp(thread, bigger)
- }
+ thread := builder.newThread(node, parent, node.IsDummy())
+ if rootLevel {
+ thread.NextSibling = parent.FirstChild
+ parent.FirstChild = thread
+ } else {
+ parent.InsertCmp(thread, bigger)
}
builder.buildTree(node.GetChild(), thread, bigger, node.IsDummy())
}
}
-func (builder *ThreadBuilder) newThread(c jwz.Threadable, parent *types.Thread) *types.Thread {
+func (builder *ThreadBuilder) newThread(c jwz.Threadable, parent *types.Thread,
+ hidden bool,
+) *types.Thread {
+ hide := 0
+ if hidden {
+ hide += 1
+ }
if threadable, ok := c.(*threadable); ok {
- return &types.Thread{Uid: threadable.MsgInfo.Uid, Parent: parent}
+ return &types.Thread{
+ Uid: threadable.UID(),
+ Parent: parent,
+ Hidden: hide,
+ }
}
return nil
}
@@ -297,6 +304,13 @@ func cleanRefs(m, irp string, refs []string) []string {
return cleanRefs
}
+func (t *threadable) UID() uint32 {
+ if t.MsgInfo == nil {
+ return 0
+ }
+ return t.MsgInfo.Uid
+}
+
func (t *threadable) Subject() string {
// deactivate threading by subject for now
return ""
diff --git a/models/templates.go b/models/templates.go
index c9b5d5b3..757e934a 100644
--- a/models/templates.go
+++ b/models/templates.go
@@ -25,6 +25,7 @@ type TemplateData interface {
ThreadUnread() int
ThreadFolded() bool
ThreadContext() bool
+ ThreadOrphan() bool
Subject() string
SubjectBase() string
Number() int
diff --git a/worker/types/thread.go b/worker/types/thread.go
index a79a0b2d..42565964 100644
--- a/worker/types/thread.go
+++ b/worker/types/thread.go
@@ -146,6 +146,9 @@ func getMaxUID(thread *Thread) uint32 {
var Uid uint32
_ = thread.Walk(func(t *Thread, _ int, currentErr error) error {
+ if t.Deleted || t.Hidden > 0 {
+ return nil
+ }
if t.Uid > Uid {
Uid = t.Uid
}