Merge branch 'master' of github.com:djcb/mu

This commit is contained in:
djcb
2014-09-01 20:13:57 -07:00
46 changed files with 617 additions and 141 deletions

View File

@ -52,6 +52,7 @@ mu_container_new (MuMsg *msg, guint docid, const char *msgid)
if (msg)
c->msg = mu_msg_ref (msg);
c->leader = c;
c->docid = docid;
c->msgid = msgid;
@ -263,9 +264,24 @@ mu_container_foreach (MuContainer *c, MuContainerForeachFunc func,
return func (c, user_data);
}
MuContainer*
mu_container_splice_children (MuContainer *c, MuContainer *sibling)
{
MuContainer *children;
g_return_val_if_fail (c, NULL);
g_return_val_if_fail (sibling, NULL);
children = sibling->child;
sibling->child = NULL;
c = mu_container_remove_sibling (c, sibling);
return mu_container_append_siblings (c, children);
}
MuContainer*
mu_container_splice_children (MuContainer *parent, MuContainer *child)
mu_container_splice_grandchildren (MuContainer *parent, MuContainer *child)
{
MuContainer *newchild;
@ -293,17 +309,28 @@ mu_container_to_list (MuContainer *c)
return lst;
}
static gpointer
list_last_data (GSList *lst)
{
GSList *tail;
tail = g_slist_last (lst);
return tail->data;
}
static MuContainer*
mu_container_from_list (GSList *lst)
{
MuContainer *c, *cur;
MuContainer *c, *cur, *tail;
if (!lst)
return NULL;
tail = list_last_data (lst);
for (c = cur = (MuContainer*)lst->data; cur; lst = g_slist_next(lst)) {
cur->next = lst ? (MuContainer*)lst->data : NULL;
cur->last = tail;
cur=cur->next;
}
@ -317,47 +344,54 @@ struct _SortFuncData {
};
typedef struct _SortFuncData SortFuncData;
static MuContainer*
get_top_msg (MuContainer *c, MuMsgFieldId mfid)
static int
container_cmp (MuContainer *a, MuContainer *b, MuMsgFieldId mfid)
{
MuContainer *piv, *extreme = c;
for (piv = c; piv != NULL && piv->msg != NULL; piv = piv->child) {
if (mu_msg_cmp (piv->msg, extreme->msg, mfid) > 0)
extreme = piv;
if (piv != c && piv->next) {
MuContainer *sub = get_top_msg (piv->next, mfid);
if (a == b)
return 0;
else if (!a->msg)
return -1;
else if (!b->msg)
return 1;
if (sub->msg != NULL && mu_msg_cmp (sub->msg, extreme->msg, mfid) > 0)
extreme = sub;
}
}
return extreme;
return mu_msg_cmp (a->msg, b->msg, mfid);
}
static gboolean
container_is_leaf (const MuContainer *c)
{
return c->child == NULL;
}
static MuContainer*
container_max (MuContainer *a, MuContainer *b, MuMsgFieldId mfid)
{
return container_cmp (a, b, mfid) > 0 ? a : b;
}
static MuContainer*
find_sorted_tree_leader (MuContainer *root, SortFuncData *order)
{
MuContainer *last_child;
if (container_is_leaf (root))
return root;
if (!order->descending)
last_child = root->child->last;
else /* reversed order, first is last */
last_child = root->child;
return container_max (root, last_child->leader, order->mfid);
}
static int
sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data)
{
MuContainer *a1, *b1;
/* use the first non-empty 'left child' message if this one
* is */
for (a1 = a; a1->msg == NULL && a1->child != NULL; a1 = a1->child);
for (b1 = b; b1->msg == NULL && b1->child != NULL; b1 = b1->child);
a1 = get_top_msg (a1, data->mfid);
b1 = get_top_msg (b1, data->mfid);
if (a1 == b1)
return 0;
else if (!a1->msg)
return 1;
else if (!b1->msg)
return -1;
if (data->descending)
return mu_msg_cmp (b1->msg, a1->msg, data->mfid);
return container_cmp (b->leader, a->leader, data->mfid);
else
return mu_msg_cmp (a1->msg, b1->msg, data->mfid);
return container_cmp (a->leader, b->leader, data->mfid);
}
static MuContainer*
@ -369,9 +403,11 @@ container_sort_real (MuContainer *c, SortFuncData *sfdata)
if (!c)
return NULL;
for (cur = c; cur; cur = cur->next)
for (cur = c; cur; cur = cur->next) {
if (cur->child)
cur->child = container_sort_real (cur->child, sfdata);
cur->leader = find_sorted_tree_leader (cur, sfdata);
}
/* sort siblings */
lst = mu_container_to_list (c);
@ -384,7 +420,6 @@ container_sort_real (MuContainer *c, SortFuncData *sfdata)
return c;
}
MuContainer*
mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean descending,
gpointer user_data)

View File

@ -45,6 +45,11 @@ struct _MuContainer {
* */
struct _MuContainer *last;
/* Node in the subtree rooted at this node which comes first
* in the descending sort order, e.g. the latest message if
* sorting by date. We compare the leaders when ordering
* subtrees. */
struct _MuContainer *leader;
MuMsg *msg;
const char *msgid;
@ -122,6 +127,19 @@ MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child);
*/
MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling);
/**
* promote sibling's children to be this container's siblings and
* remove the sibling
*
* @param c a container instance
* @param sibling a sibling of this container
*
* @return the container with the sibling's children promoted and the
* sibling itself removed
*/
MuContainer* mu_container_splice_children (MuContainer *c,
MuContainer *sibling);
/**
* promote child's children to be parent's children and remove child
@ -131,8 +149,8 @@ MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling);
*
* @return the new container with it's children's children promoted
*/
MuContainer* mu_container_splice_children (MuContainer *parent,
MuContainer *child);
MuContainer* mu_container_splice_grandchildren (MuContainer *parent,
MuContainer *child);
typedef gboolean (*MuContainerForeachFunc) (MuContainer*, gpointer);

View File

@ -388,7 +388,7 @@ prune_maybe (MuContainer *c)
if (cur->flags & MU_CONTAINER_FLAG_DELETE)
c = mu_container_remove_child (c, cur);
else if (cur->flags & MU_CONTAINER_FLAG_SPLICE)
c = mu_container_splice_children (c, cur);
c = mu_container_splice_grandchildren (c, cur);
}
g_return_val_if_fail (c, FALSE);
@ -433,17 +433,10 @@ prune_empty_containers (MuContainer *root_set)
/* and prune the root_set itself... */
for (cur = root_set; cur; cur = cur->next) {
if (cur->flags & MU_CONTAINER_FLAG_DELETE)
root_set = mu_container_remove_sibling (root_set, cur);
else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) {
MuContainer *newchild;
newchild = cur->child;
cur->child = NULL;
root_set = mu_container_append_siblings (root_set,
newchild);
}
else if (cur->flags & MU_CONTAINER_FLAG_SPLICE)
root_set = mu_container_splice_children (root_set, cur);
}
return root_set;

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: A
Message-Id: <A@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
A

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: B
Message-Id: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
B

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: C
Message-Id: <C@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
C

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: D
Message-Id: <D@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
D

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: A
Message-Id: <A@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
A

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: B
Message-Id: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
B

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: C
Message-Id: <C@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
C

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: D
Message-Id: <D@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
D

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: E
Message-Id: <E@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
E

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: A
Message-Id: <A@msg.id>
References: <Y@msg.id>
In-reply-to: <Y@msg.id>
Aate: Sat, 17 May 2014 10:00:00 +0000
A

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: X
Message-Id: <X@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
X

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: Y
Message-Id: <Y@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
Y

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: Z
Message-Id: <Z@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
Z

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: A
Message-Id: <A@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
A

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: B
Message-Id: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
B

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: C
Message-Id: <C@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
C

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: D
Message-Id: <D@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
D

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: E
Message-Id: <E@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
E

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: F
Message-Id: <F@msg.id>
References: <B@msg.id> <D@msg.id>
In-reply-to: <D@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
F

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: G
Message-Id: <G@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
G

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: A
Message-Id: <A@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
A

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: B
Message-Id: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
B

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: C
Message-Id: <C@msg.id>
References: <B@msg.id>
In-reply-to: <B@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
C

View File

@ -0,0 +1,7 @@
From: testfrom@example.com
To: testto@example.com
Subject: D
Message-Id: <D@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
D

View File

@ -0,0 +1,9 @@
From: testfrom@example.com
To: testto@example.com
Subject: E
Message-Id: <E@msg.id>
References: <B@msg.id> <C@msg.id>
In-reply-to: <C@msg.id>
Date: Sat, 17 May 2014 10:00:00 +0000
E

View File

@ -133,7 +133,7 @@ of the message, as listed in the following table:
p,passed Passed ('Handled')
r,replied Replied
s,seen Seen
t,thrashed Marked for deletion
t,trashed Marked for deletion
a,attach Has attachment
z,signed Signed message
x,encrypted Encrypted message

View File

@ -34,6 +34,65 @@
#include "mu-query.h"
#include "mu-str.h"
struct _tinfo {
const char *threadpath;
const char *msgid;
const char *subject;
};
typedef struct _tinfo tinfo;
static void
assert_tinfo_equal (const tinfo *expected, const tinfo *actual)
{
g_assert_cmpstr (expected->threadpath,==,actual->threadpath);
g_assert_cmpstr (expected->subject,==,actual->subject);
g_assert_cmpstr (expected->msgid,==,actual->msgid);
}
static void
tinfo_init_from_iter (tinfo *item, MuMsgIter *iter)
{
MuMsg *msg;
const MuMsgIterThreadInfo *ti;
msg = mu_msg_iter_get_msg_floating (iter);
g_assert (msg);
ti = mu_msg_iter_get_thread_info (iter);
if (!ti)
g_print ("%s: thread info not found\n", mu_msg_get_msgid (msg));
g_assert (ti);
item->threadpath = ti->threadpath;
item->subject = mu_msg_get_subject (msg);
item->msgid = mu_msg_get_msgid (msg);
if (g_test_verbose())
g_print ("%s %s %s\n",
item->threadpath, item->subject, item->msgid);
}
static void
foreach_assert_tinfo_equal (MuMsgIter *iter, const tinfo items[], guint n_items)
{
guint u;
u = 0;
while (!mu_msg_iter_is_done (iter) && u < n_items) {
tinfo ti;
tinfo_init_from_iter (&ti, iter);
g_assert (u < n_items);
assert_tinfo_equal (&items[u], &ti);
++u;
mu_msg_iter_next (iter);
}
g_assert (u == n_items);
}
static gchar*
fill_database (const char *testdir)
{
@ -58,7 +117,8 @@ fill_database (const char *testdir)
/* note: this also *moves the iter* */
static MuMsgIter*
run_and_get_iter (const char *xpath, const char *query)
run_and_get_iter_full (const char *xpath, const char *query,
MuMsgFieldId sort_field, MuQueryFlags flags)
{
MuQuery *mquery;
MuStore *store;
@ -71,27 +131,28 @@ run_and_get_iter (const char *xpath, const char *query)
mu_store_unref (store);
g_assert (query);
iter = mu_query_run (mquery, query, MU_MSG_FIELD_ID_DATE,
-1, MU_QUERY_FLAG_THREADS, NULL);
flags |= MU_QUERY_FLAG_THREADS;
iter = mu_query_run (mquery, query, sort_field, -1, flags, NULL);
mu_query_destroy (mquery);
g_assert (iter);
return iter;
}
static MuMsgIter*
run_and_get_iter (const char *xpath, const char *query)
{
return run_and_get_iter_full (xpath, query, MU_MSG_FIELD_ID_DATE,
MU_QUERY_FLAG_NONE);
}
static void
test_mu_threads_01 (void)
{
gchar *xpath;
MuMsgIter *iter;
unsigned u;
struct {
const char* threadpath;
const char *msgid;
const char* subject;
} items [] = {
const tinfo items [] = {
{"0", "root0@msg.id", "root0"},
{"0:0", "child0.0@msg.id", "Re: child 0.0"},
{"0:1", "child0.1@msg.id", "Re: child 0.1"},
@ -115,54 +176,17 @@ test_mu_threads_01 (void)
g_assert (iter);
g_assert (!mu_msg_iter_is_done(iter));
u = 0;
while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items)) {
MuMsg *msg;
const MuMsgIterThreadInfo *ti;
ti = mu_msg_iter_get_thread_info (iter);
if (!ti)
g_print ("%s: thread info not found for %u\n",
__FUNCTION__, (unsigned)mu_msg_iter_get_docid(iter));
g_assert(ti);
msg = mu_msg_iter_get_msg_floating (iter);
g_assert (msg);
if (g_test_verbose())
g_print ("%s %s %s\n", ti->threadpath,
mu_msg_get_msgid(msg),
mu_msg_get_path (msg));
g_assert (u < G_N_ELEMENTS(items));
g_assert_cmpstr (ti->threadpath,==,items[u].threadpath);
g_assert_cmpstr (mu_msg_get_subject(msg),==,items[u].subject);
g_assert_cmpstr (mu_msg_get_msgid(msg),==,items[u].msgid);
++u;
mu_msg_iter_next (iter);
}
g_assert (u == G_N_ELEMENTS(items));
foreach_assert_tinfo_equal (iter, items, G_N_ELEMENTS (items));
g_free (xpath);
mu_msg_iter_destroy (iter);
}
struct _tinfo {
const char* threadpath;
const char *msgid;
const char* subject;
};
typedef struct _tinfo tinfo;
static void
test_mu_threads_rogue (void)
{
gchar *xpath;
MuMsgIter *iter;
unsigned u;
tinfo *items;
tinfo items1 [] = {
@ -194,40 +218,214 @@ test_mu_threads_rogue (void)
else
items = items2;
u = 0;
while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items1)) {
MuMsg *msg;
const MuMsgIterThreadInfo *ti;
ti = mu_msg_iter_get_thread_info (iter);
if (!ti)
g_print ("%s: thread info not found\n",
mu_msg_get_msgid(mu_msg_iter_get_msg_floating (iter)));
g_assert(ti);
msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */
/* g_print ("%s %s %s\n", ti->threadpath, */
/* mu_msg_get_msgid(msg), */
/* mu_msg_get_path (msg) */
/* ); */
g_assert (u < G_N_ELEMENTS(items1));
g_assert_cmpstr (ti->threadpath,==,(items)[u].threadpath);
g_assert_cmpstr (mu_msg_get_subject(msg),==,(items)[u].subject);
g_assert_cmpstr (mu_msg_get_msgid(msg),==,(items)[u].msgid);
++u;
mu_msg_iter_next (iter);
}
g_assert (u == G_N_ELEMENTS(items1));
foreach_assert_tinfo_equal (iter, items, G_N_ELEMENTS (items1));
g_free (xpath);
mu_msg_iter_destroy (iter);
}
static MuMsgIter*
query_testdir (const char *query, MuMsgFieldId sort_field, gboolean descending)
{
MuMsgIter *iter;
gchar *xpath;
MuQueryFlags flags;
flags = MU_QUERY_FLAG_NONE;
if (descending)
flags |= MU_QUERY_FLAG_DESCENDING;
xpath = fill_database (MU_TESTMAILDIR3);
g_assert (xpath != NULL);
iter = run_and_get_iter_full (xpath, query, sort_field, flags);
g_assert (iter != NULL);
g_assert (!mu_msg_iter_is_done (iter));
g_free (xpath);
return iter;
}
static void
check_sort_by_subject (const char *query, const tinfo expected[],
guint n_expected, gboolean descending)
{
MuMsgIter *iter;
iter = query_testdir (query, MU_MSG_FIELD_ID_SUBJECT, descending);
foreach_assert_tinfo_equal (iter, expected, n_expected);
mu_msg_iter_destroy (iter);
}
static void
check_sort_by_subject_asc (const char *query, const tinfo expected[],
guint n_expected)
{
check_sort_by_subject (query, expected, n_expected, FALSE);
}
static void
check_sort_by_subject_desc (const char *query, const tinfo expected[],
guint n_expected)
{
check_sort_by_subject (query, expected, n_expected, TRUE);
}
static void
test_mu_threads_sort_1st_child_promotes_thread (void)
{
const char *query = "maildir:/sort/1st-child-promotes-thread";
const tinfo expected_asc [] = {
{ "0", "A@msg.id", "A"},
{ "1", "C@msg.id", "C"},
{ "2", "B@msg.id", "B"},
{ "2:0", "D@msg.id", "D"},
};
const tinfo expected_desc [] = {
{ "0", "B@msg.id", "B"},
{ "0:0", "D@msg.id", "D"},
{ "1", "C@msg.id", "C"},
{ "2", "A@msg.id", "A"},
};
check_sort_by_subject_asc (query, expected_asc,
G_N_ELEMENTS (expected_asc));
check_sort_by_subject_desc (query, expected_desc,
G_N_ELEMENTS (expected_desc));
}
static void
test_mu_threads_sort_2nd_child_promotes_thread (void)
{
const char *query = "maildir:/sort/2nd-child-promotes-thread";
const tinfo expected_asc [] = {
{ "0", "A@msg.id", "A"},
{ "1", "D@msg.id", "D"},
{ "2", "B@msg.id", "B"},
{ "2:0", "C@msg.id", "C"},
{ "2:1", "E@msg.id", "E"},
};
const tinfo expected_desc [] = {
{ "0", "B@msg.id", "B"},
{ "0:0", "E@msg.id", "E"},
{ "0:1", "C@msg.id", "C"},
{ "1", "D@msg.id", "D"},
{ "2", "A@msg.id", "A"},
};
check_sort_by_subject_asc (query, expected_asc,
G_N_ELEMENTS (expected_asc));
check_sort_by_subject_desc (query, expected_desc,
G_N_ELEMENTS (expected_desc));
}
static void
test_mu_threads_sort_orphan_promotes_thread (void)
{
const char *query = "maildir:/sort/2nd-child-promotes-thread NOT B";
/* B lost, C & E orphaned but not promoted */
const tinfo expected_asc [] = {
{ "0", "A@msg.id", "A"},
{ "1", "D@msg.id", "D"},
{ "2:0", "C@msg.id", "C"},
{ "2:1", "E@msg.id", "E"},
};
const tinfo expected_desc [] = {
{ "0:0", "E@msg.id", "E"},
{ "0:1", "C@msg.id", "C"},
{ "1", "D@msg.id", "D"},
{ "2", "A@msg.id", "A"},
};
check_sort_by_subject_asc (query, expected_asc,
G_N_ELEMENTS (expected_asc));
check_sort_by_subject_desc (query, expected_desc,
G_N_ELEMENTS (expected_desc));
}
/* Won't normally happen when sorting by date. */
static void
test_mu_threads_sort_child_does_not_promote_thread (void)
{
const char *query = "maildir:/sort/child-does-not-promote-thread";
const tinfo expected_asc [] = {
{ "0", "X@msg.id", "X"},
{ "1", "Y@msg.id", "Y"},
{ "1:0", "A@msg.id", "A"},
{ "2", "Z@msg.id", "Z"},
};
const tinfo expected_desc [] = {
{ "0", "Z@msg.id", "Z"},
{ "1", "Y@msg.id", "Y"},
{ "1:0", "A@msg.id", "A"},
{ "2", "X@msg.id", "X"},
};
check_sort_by_subject_asc (query, expected_asc,
G_N_ELEMENTS (expected_asc));
check_sort_by_subject_desc (query, expected_desc,
G_N_ELEMENTS (expected_desc));
}
static void
test_mu_threads_sort_grandchild_promotes_thread (void)
{
const char *query = "maildir:/sort/grandchild-promotes-thread";
const tinfo expected_asc [] = {
{ "0", "A@msg.id", "A"},
{ "1", "D@msg.id", "D"},
{ "2", "B@msg.id", "B"},
{ "2:0", "C@msg.id", "C"},
{ "2:0:0", "E@msg.id", "E"},
};
const tinfo expected_desc [] = {
{ "0", "B@msg.id", "B"},
{ "0:0", "C@msg.id", "C"},
{ "0:0:0", "E@msg.id", "E"},
{ "1", "D@msg.id", "D"},
{ "2", "A@msg.id", "A"},
};
check_sort_by_subject_asc (query, expected_asc,
G_N_ELEMENTS (expected_asc));
check_sort_by_subject_desc (query, expected_desc,
G_N_ELEMENTS (expected_desc));
}
static void
test_mu_threads_sort_granchild_promotes_only_subthread (void)
{
const char *query = "maildir:/sort/grandchild-promotes-only-subthread";
const tinfo expected_asc [] = {
{ "0", "A@msg.id", "A"},
{ "1", "B@msg.id", "B"},
{ "1:0", "C@msg.id", "C"},
{ "1:1", "E@msg.id", "E"},
{ "1:2", "D@msg.id", "D"},
{ "1:2:0", "F@msg.id", "F"},
{ "2", "G@msg.id", "G"},
};
const tinfo expected_desc [] = {
{ "0", "G@msg.id", "G"},
{ "1", "B@msg.id", "B"},
{ "1:0", "D@msg.id", "D"},
{ "1:0:0", "F@msg.id", "F"},
{ "1:1", "E@msg.id", "E"},
{ "1:2", "C@msg.id", "C"},
{ "2", "A@msg.id", "A"},
};
check_sort_by_subject_asc (query, expected_asc,
G_N_ELEMENTS (expected_asc));
check_sort_by_subject_desc (query, expected_desc,
G_N_ELEMENTS (expected_desc));
}
int
main (int argc, char *argv[])
{
@ -237,6 +435,18 @@ main (int argc, char *argv[])
g_test_add_func ("/mu-query/test-mu-threads-01", test_mu_threads_01);
g_test_add_func ("/mu-query/test-mu-threads-rogue", test_mu_threads_rogue);
g_test_add_func ("/mu-query/test-mu-threads-sort-1st-child-promotes-thread",
test_mu_threads_sort_1st_child_promotes_thread);
g_test_add_func ("/mu-query/test-mu-threads-sort-2nd-child-promotes-thread",
test_mu_threads_sort_2nd_child_promotes_thread);
g_test_add_func ("/mu-query/test-mu-threads-orphan-promotes-thread",
test_mu_threads_sort_orphan_promotes_thread);
g_test_add_func ("/mu-query/test-mu-threads-sort-child-does-not-promote-thread",
test_mu_threads_sort_child_does_not_promote_thread);
g_test_add_func ("/mu-query/test-mu-threads-sort-grandchild-promotes-thread",
test_mu_threads_sort_grandchild_promotes_thread);
g_test_add_func ("/mu-query/test-mu-threads-sort-grandchild-promotes-only-subthread",
test_mu_threads_sort_granchild_promotes_only_subthread);
g_log_set_handler (NULL,
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,

View File

@ -326,7 +326,7 @@ appear on disk."
mu4e~compose-buffer-max-name-length
nil nil t)))))
(defun mu4e~compose-handler (compose-type &optional original-msg includes)
(defun* mu4e~compose-handler (compose-type &optional original-msg includes)
"Create a new draft message, or open an existing one.
COMPOSE-TYPE determines the kind of message to compose and is a
@ -352,7 +352,10 @@ tempfile)."
(run-hooks 'mu4e-compose-pre-hook)
;; this opens (or re-opens) a messages with all the basic headers set.
(mu4e-draft-open compose-type original-msg)
(condition-case nil
(mu4e-draft-open compose-type original-msg)
(quit (kill-buffer) (message "[mu4e] Operation aborted")
(return-from mu4e~compose-handler)))
;; insert mail-header-separator, which is needed by message mode to separate
;; headers and body. will be removed before saving to disk
(mu4e~draft-insert-mail-header-separator)

View File

@ -31,6 +31,7 @@
("boost-announce.lists.boost.org" . "BoostA")
("boost-interest.lists.boost.org" . "BoostI")
("conkeror.mozdev.org" . "Conkeror")
("curl-library.cool.haxx.se" . "LibCurl")
("crypto-gram-list.schneier.com " . "CryptoGr")
("dbus.lists.freedesktop.org" . "DBus")
("desktop-devel-list.gnome.org" . "GnomeDT")

View File

@ -64,11 +64,9 @@
"Major mode for the mu4e main screen.
\\{mu4e-main-mode-map}."
(use-local-map mu4e-main-mode-map)
(setq
truncate-lines t
overwrite-mode 'overwrite-mode-binary
revert-buffer-function 'mu4e:main-revert-buffer
))
(setq truncate-lines t
overwrite-mode 'overwrite-mode-binary)
(set (make-local-variable 'revert-buffer-function) #'mu4e:main-revert-buffer))
(defun mu4e~main-action-str (str &optional func-or-shortcut)

View File

@ -138,7 +138,7 @@ The following marks are available, and the corresponding props:
`flag' n mark this message for flagging
`move' y move the message to some folder
`read' n mark the message as read
`trash' y thrash the message to some folder
`trash' y trash the message to some folder
`unflag' n mark this message for unflagging
`untrash' n remove the 'trashed' flag from a message
`unmark' n unmark this message
@ -286,7 +286,7 @@ user which one)."
mu4e~mark-map))))
(defun mu4e~mark-check-target (target)
"Check if the target exists if not, offer to create it."
"Check if the target exists; if not, offer to create it."
(let ((fulltarget (concat mu4e-maildir target)))
(if (not (mu4e-create-maildir-maybe fulltarget))
(mu4e-error "Target dir %s does not exist " fulltarget)
@ -298,9 +298,9 @@ user which one)."
After the actions have been executed succesfully, the affected
messages are *hidden* from the current header list. Since the
headers are the result of a search, we cannot be certain that the
messages no longer matches the current one - to get that
messages no longer match the current one - to get that
certainty, we need to rerun the search, but we don't want to do
that automatically, as it may be too slow and/or break the users
that automatically, as it may be too slow and/or break the user's
flow. Therefore, we hide the message, which in practice seems to
work well.

View File

@ -597,6 +597,7 @@ FUNC should be a function taking two arguments:
;; misc
(define-key map "w" 'visual-line-mode)
(define-key map "h" 'mu4e-view-toggle-hide-cited)
(define-key map (kbd "M-q") 'mu4e-view-fill-long-lines)
;; next 3 only warn user when attempt in the message view
(define-key map "u" 'mu4e-view-unmark)
@ -909,6 +910,28 @@ Add this function to `mu4e-view-mode-hook' to enable this feature."
(overlay-put ov 'face 'mu4e-region-code))
(setq beg nil end nil))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Wash functions
(defun mu4e-view-fill-long-lines ()
"Fill lines that are wider than the window width or `fill-column'."
(interactive)
(with-current-buffer mu4e~view-buffer
(save-excursion
(let ((inhibit-read-only t)
(width (window-width (get-buffer-window (current-buffer)))))
(save-restriction
(message-goto-body)
(while (not (eobp))
(end-of-line)
(when (>= (current-column) (min fill-column width))
(narrow-to-region (min (1+ (point)) (point-max))
(point-at-bol))
(let ((goback (point-marker)))
(fill-paragraph nil)
(goto-char (marker-position goback)))
(widen))
(forward-line 1)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; attachment handling
(defun mu4e~view-get-attach-num (prompt msg &optional multi)

View File

@ -810,7 +810,7 @@ W toggle include-related
marking
-------
d mark for moving to the trash folder
= mark for remove thrash flags ('untrash')
= mark for removing trash flag ('untrash')
DEL,D mark for complete deletion
m mark for moving to another maildir folder
r mark for refiling
@ -1076,7 +1076,7 @@ M-right next query
marking
-------
d mark for moving to the trash folder
= mark for remove thrash flags ('untrash')
= mark for removing trash flag ('untrash')
DEL,D mark for complete deletion
m mark for moving to another maildir folder
r mark for refiling
@ -1839,12 +1839,12 @@ previous/next queries, you can use @kbd{M-x mu4e-headers-forget-queries}.
It can be useful to narrow existing search results, that is, to add some
clauses to the current query to match fewer messages.
For example, suppose you're looking at the some mailing list, perhaps by
For example, suppose you're looking at some mailing list, perhaps by
jumping to a maildir (@kbd{M-x mu4e-headers-jump-to-maildir}, @key{j}) or
because you followed some bookmark (@kbd{M-x mu4e-headers-search-bookmark},
@key{b}). Now, you want to narrow things down to only those messages that have
attachments.
1
This is when @kbd{M-x mu4e-headers-search-narrow} (@key{/}) comes in handy. It
asks for an additional search pattern, which is appended to the current search
query, in effect getting you the subset of the currently shown headers that
@ -3234,10 +3234,10 @@ value)} pairs:
("Account2"
(mu4e-sent-folder "/Account2/Saved Items")
(mu4e-drafts-folder "/Account2/Drafts")
(user-mail-address "my.address@@account2.tld)
(smtpmail-default-smtp-server "smtp.account2.tld)
(user-mail-address "my.address@@account2.tld")
(smtpmail-default-smtp-server "smtp.account2.tld")
(smtpmail-local-domain "account2.tld")
(smtpmail-smtp-server "smtp.account2.tld)
(smtpmail-smtp-server "smtp.account2.tld")
(smtpmail-stream-type starttls)
(smtpmail-smtp-service 587))))
@end lisp