unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
From: David Bremner <david@tethera.net>
To: notmuch@notmuchmail.org
Subject: [PATCH 06/15] lib/thread: refactor in-reply-to test.
Date: Tue, 31 Jul 2018 06:45:46 +0800	[thread overview]
Message-ID: <20180730224555.26047-7-david@tethera.net> (raw)
In-Reply-To: <20180730224555.26047-1-david@tethera.net>

This is not a complete win in code-size, but it makes the code (which
is about to get more complicated) easier to follow. In particular the
second pass (which looks a bit wasteful here) will be needed when we
reparent by references.
---
 lib/thread.cc               | 107 ++++++++++++++++++++++++++----------
 test/T510-thread-replies.sh |   1 -
 2 files changed, 77 insertions(+), 31 deletions(-)

diff --git a/lib/thread.cc b/lib/thread.cc
index db592a3a..9f923843 100644
--- a/lib/thread.cc
+++ b/lib/thread.cc
@@ -387,29 +387,72 @@ _thread_add_matched_message (notmuch_thread_t *thread,
     _thread_add_matched_author (thread, _notmuch_message_get_author (hashed_message));
 }
 
+static bool
+_parent_via_in_reply_to (notmuch_thread_t *thread, notmuch_message_t *message) {
+    notmuch_message_t *parent;
+    const char *in_reply_to;
+
+    in_reply_to = _notmuch_message_get_in_reply_to (message);
+    DEBUG_PRINTF("checking message = %s in_reply_to=%s\n",
+		 notmuch_message_get_message_id (message), in_reply_to);
+
+    if (in_reply_to && strlen (in_reply_to) &&
+	g_hash_table_lookup_extended (thread->message_hash,
+				      in_reply_to, NULL,
+				      (void **) &parent)) {
+	_notmuch_message_add_reply (parent, message);
+	return true;
+    } else {
+	return false;
+    }
+}
+
+static void
+_parent_or_toplevel (notmuch_thread_t *thread, notmuch_message_t *message)
+{
+    bool found = false;
+    notmuch_message_t *parent = NULL;
+    const notmuch_string_list_t *references =
+	_notmuch_message_get_references (message);
+    for (notmuch_string_node_t *ref_node = references->head;
+	 ! found && ref_node; ref_node = ref_node->next) {
+	if ((found = g_hash_table_lookup_extended (thread->message_hash,
+						   ref_node->string, NULL,
+						   (void **) &parent))) {
+	    _notmuch_message_add_reply (parent, message);
+	}
+    }
+    if (! found)
+	_notmuch_message_list_add_message (thread->toplevel_list, message);
+}
+
 static void
 _resolve_thread_relationships (notmuch_thread_t *thread)
 {
     notmuch_message_node_t *node, *first_node;
-    notmuch_message_t *message, *parent;
-    const char *in_reply_to;
+    notmuch_message_t *message;
+    void *local;
+    notmuch_message_list_t *maybe_toplevel_list;
 
     first_node = thread->message_list->head;
     if (! first_node)
 	return;
 
+    local = talloc_new (thread);
+    maybe_toplevel_list = _notmuch_message_list_create (local);
+
     for (node = first_node->next; node; node = node->next) {
 	message = node->message;
-	in_reply_to = _notmuch_message_get_in_reply_to (message);
-	if (in_reply_to && strlen (in_reply_to) &&
-	    g_hash_table_lookup_extended (thread->message_hash,
-					  in_reply_to, NULL,
-					  (void **) &parent))
-	    _notmuch_message_add_reply (parent, message);
-	else
-	    _notmuch_message_list_add_message (thread->toplevel_list, message);
+	if (! _parent_via_in_reply_to (thread, message))
+	    _notmuch_message_list_add_message (maybe_toplevel_list, message);
     }
 
+    for (notmuch_messages_t *roots = _notmuch_messages_create (maybe_toplevel_list);
+	 notmuch_messages_valid (roots);
+	 notmuch_messages_move_to_next (roots)) {
+	notmuch_message_t *message = notmuch_messages_get (roots);
+	_parent_or_toplevel (thread, message);
+    }
     /*
      * if we reach the end of the list without finding a top-level
      * message, that means the thread is a cycle (or set of cycles)
@@ -418,17 +461,31 @@ _resolve_thread_relationships (notmuch_thread_t *thread)
      */
     if (first_node) {
 	message = first_node->message;
-	in_reply_to = _notmuch_message_get_in_reply_to (message);
-	if (thread->toplevel_list->head &&
-	    in_reply_to && strlen (in_reply_to) &&
-	    g_hash_table_lookup_extended (thread->message_hash,
-					  in_reply_to, NULL,
-					  (void **) &parent))
-	    _notmuch_message_add_reply (parent, message);
-	else
-	    _notmuch_message_list_add_message (thread->toplevel_list, message);
+	if (! thread->toplevel_list->head ||
+	    ! _parent_via_in_reply_to (thread, message)) {
+	    /*
+	     * If the oldest message happens to be in-reply-to a
+	     * missing message, we only check for references if there
+	     * is some other candidate for root message.
+	     */
+	    if (thread->toplevel_list->head)
+		_parent_or_toplevel (thread, message);
+	    else
+		_notmuch_message_list_add_message (thread->toplevel_list, message);
+	}
     }
 
+    /* XXX: After scanning through the entire list looking for parents
+     * via "In-Reply-To", we should do a second pass that looks at the
+     * list of messages IDs in the "References" header instead.
+     * Unlike the current quick fix, the parent should be the
+     * "deepest" message of all the messages found in the "References"
+     * list.
+     *
+     * Doing this will allow messages and sub-threads to be positioned
+     * correctly in the thread even when an intermediate message is
+     * missing from the thread.
+     */
     for (notmuch_messages_t *messages = _notmuch_messages_create (thread->toplevel_list);
 	 notmuch_messages_valid (messages);
 	 notmuch_messages_move_to_next (messages))
@@ -437,17 +494,7 @@ _resolve_thread_relationships (notmuch_thread_t *thread)
 	_notmuch_message_sort_subtree (message);
     }
 
-
-    /* XXX: After scanning through the entire list looking for parents
-     * via "In-Reply-To", we should do a second pass that looks at the
-     * list of messages IDs in the "References" header instead. (And
-     * for this the parent would be the "deepest" message of all the
-     * messages found in the "References" list.)
-     *
-     * Doing this will allow messages and sub-threads to be positioned
-     * correctly in the thread even when an intermediate message is
-     * missing from the thread.
-     */
+    talloc_free (local);
 }
 
 /* Create a new notmuch_thread_t object by finding the thread
diff --git a/test/T510-thread-replies.sh b/test/T510-thread-replies.sh
index b7322198..3ee2ee78 100755
--- a/test/T510-thread-replies.sh
+++ b/test/T510-thread-replies.sh
@@ -167,7 +167,6 @@ test_expect_equal_json "$output" "$expected"
 add_email_corpus threading
 
 test_begin_subtest "reply to ghost"
-test_subtest_known_broken
 notmuch show --entire-thread=true id:000-real-root@example.org | grep ^Subject: | head -1  > OUTPUT
 cat <<EOF > EXPECTED
 Subject: root message
-- 
2.18.0

  parent reply	other threads:[~2018-07-30 22:46 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-30 22:45 Threading patches v2 David Bremner
2018-07-30 22:45 ` [PATCH 01/15] util: add DEBUG_PRINTF, rename error_util.h -> debug_print.h David Bremner
2018-07-30 22:45 ` [PATCH 02/15] test: start threading test corpus David Bremner
2018-07-30 22:45 ` [PATCH 03/15] test: add known broken tests for "ghost roots" David Bremner
2018-07-30 22:45 ` [PATCH 04/15] lib/thread: sort child messages by date David Bremner
2018-07-30 22:45 ` [PATCH 05/15] lib: read reference terms into message struct David Bremner
2018-07-30 22:45 ` David Bremner [this message]
2018-07-30 22:45 ` [PATCH 07/15] lib: calculate message depth in thread David Bremner
2018-07-30 22:45 ` [PATCH 08/15] lib/thread: rewrite _parent_or_toplevel to use depths David Bremner
2018-07-30 22:45 ` [PATCH 09/15] lib/thread: change _resolve_thread_relationships " David Bremner
2018-07-30 22:45 ` [PATCH 10/15] test: add known broken test for good In-Reply-To / bad References David Bremner
2018-07-30 22:45 ` [PATCH 11/15] test/thread-replies: mangle In-Reply-To's David Bremner
2018-07-30 22:45 ` [PATCH 12/15] util/string-util: export skip_space David Bremner
2018-07-30 22:45 ` [PATCH 13/15] lib: add _notmuch_message_id_parse_strict David Bremner
2018-08-01  0:46   ` Amin Bandali
2018-08-01  4:58   ` [Patch v1.1] " David Bremner
2018-07-30 22:45 ` [PATCH 14/15] lib: change parent strategy to use In-Reply-To if it looks sane David Bremner
2018-07-30 22:45 ` [PATCH 15/15] test: add known broken test for multiple thread terms per message David Bremner
2018-08-01 14:53 ` Threading patches v2 Gregor Zattler
2018-08-27  1:53   ` [PATCH] WIP: sort top level messages in thread David Bremner
2018-08-27 13:44     ` Gregor Zattler
2018-08-28 21:33       ` Amin Bandali

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://notmuchmail.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180730224555.26047-7-david@tethera.net \
    --to=david@tethera.net \
    --cc=notmuch@notmuchmail.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).