[RFC] reordering and cleanup of thread authors

Dirk Hohndel hohndel at infradead.org
Tue Apr 6 22:52:28 PDT 2010


This is based in part on some discussion on IRC today.
When a thread is displayed in the search results, previously the authors
were always displayed in chronological order. But if only part of the
thread matches the query, that may or may not be the intuitive thing to
do.
Imagine the default "+inbox" query. Those mails in the thread that
match the query are actually "new" (whatever that means). And some
people seem to think that it would be much better to see those author
names first. For example, imagine a long and drawn out thread that once
was started by me; you have long read the older part of the thread and
removed the inbox tag. Whenever a new email comes in on this thread, the
author column in the search display will first show "Dirk Hohndel" - I
think it should first show the actual author(s) of the new mail(s).

The second cleanup that I've done in this context is to convert all
author names into the much easier to parse "Firstname Lastname"
format. Some broken mail systems (cough - Exchange - Cough) seem to
prefer "Lastname, Firstname" - which makes the comma separated list of
authors really hard to parse at one glance. Worse, it creates douplicate
entries if a person happens to show in both orders. So this does a
rather simplistic "look for a comma, if it's there, reorder" approach to
cleaning up the author name.

I don't think this is ready to be pulled. There was a strong request on
IRC to make the re-ordering of the author names configurable. I think
the cleanup of the names (First Last) should be configurable as well.
And of course I wonder about my implementation, given that I'm still
trying to wrap my mind around the current code base.

Thanks

/D

diff --git a/lib/message.cc b/lib/message.cc
index 721c9a6..ac0afd9 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -35,6 +35,7 @@ struct _notmuch_message {
     char *thread_id;
     char *in_reply_to;
     char *filename;
+    char *author;
     notmuch_message_file_t *message_file;
     notmuch_message_list_t *replies;
     unsigned long flags;
@@ -110,6 +111,7 @@ _notmuch_message_create (const void *talloc_owner,
     message->in_reply_to = NULL;
     message->filename = NULL;
     message->message_file = NULL;
+    message->author = NULL;
 
     message->replies = _notmuch_message_list_create (message);
     if (unlikely (message->replies == NULL)) {
@@ -533,6 +535,20 @@ notmuch_message_get_tags (notmuch_message_t *message)
     return _notmuch_convert_tags(message, i, end);
 }
 
+const char *
+notmuch_message_get_author (notmuch_message_t *message)
+{
+    return message->author;
+}
+
+void
+notmuch_message_set_author (notmuch_message_t *message,
+			    const char *author)
+{
+    message->author = talloc_strdup(message, author);
+    return;
+}
+
 void
 _notmuch_message_set_date (notmuch_message_t *message,
 			   const char *date)
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 88da078..79638bb 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -771,6 +771,17 @@ notmuch_message_set_flag (notmuch_message_t *message,
 time_t
 notmuch_message_get_date  (notmuch_message_t *message);
 
+/* Set the author member of 'message' - this is the representation used
+ * when displaying the message
+ */
+void
+notmuch_message_set_author (notmuch_message_t *message, const char *author);
+
+/* Get the author member of 'message'
+ */
+const char *
+notmuch_message_get_author (notmuch_message_t *message);
+
 /* Get the value of the specified header from 'message'.
  *
  * The value will be read from the actual message file, not from the
diff --git a/lib/thread.cc b/lib/thread.cc
index 48c070e..c3c83a3 100644
--- a/lib/thread.cc
+++ b/lib/thread.cc
@@ -32,6 +32,7 @@ struct _notmuch_thread {
     char *subject;
     GHashTable *authors_hash;
     char *authors;
+    char *nonmatched_authors;
     GHashTable *tags;
 
     notmuch_message_list_t *message_list;
@@ -69,8 +70,95 @@ _thread_add_author (notmuch_thread_t *thread,
 	thread->authors = talloc_asprintf (thread, "%s, %s",
 					   thread->authors,
 					   author);
-    else
+    else {
 	thread->authors = talloc_strdup (thread, author);
+    }
+}
+
+/*
+ * move authors of matched messages in the thread to 
+ * the front of the authors list, but keep them in
+ * oldest first order within their group
+ */
+static void
+_thread_move_matched_author (notmuch_thread_t *thread,
+			     const char *author)
+{
+    char *authorscopy;
+    char *currentauthor;
+    int idx,nmstart,author_len,authors_len;
+
+    if (thread->authors == NULL)
+	return;
+    if (thread->nonmatched_authors == NULL)
+	thread->nonmatched_authors = thread->authors;
+    author_len = strlen(author);
+    authors_len = strlen(thread->authors);
+    if (author_len == authors_len) {
+	/* just one author */
+	thread->nonmatched_authors += author_len;
+	return;
+    }
+    currentauthor = strcasestr(thread->authors, author);
+    if (currentauthor == NULL)
+	return;
+    idx = currentauthor - thread->nonmatched_authors;
+    if (idx < 0) {
+	/* already among matched authors */
+	return;
+    }
+    if (thread->nonmatched_authors + author_len < thread->authors + authors_len) {
+	/* we have to make changes, so let's get a temp copy */
+	authorscopy = strdup(thread->authors);
+	if (authorscopy == NULL)
+	    return;
+	/* nmstart is the offset into where the non-matched authors start */
+	nmstart = thread->nonmatched_authors - thread->authors;
+	/* copy this author and add the "| " - the if clause above tells us there's more */
+	strncpy(thread->nonmatched_authors,author,author_len);
+	strncpy(thread->nonmatched_authors+author_len,"| ",2);
+	thread->nonmatched_authors += author_len+2;	
+	if (idx > 0) {
+	  /* we are actually moving authors around, not just changing the separator
+	   * first copy the authors that came BEFORE our author */
+	  strncpy(thread->nonmatched_authors, authorscopy+nmstart, idx-2);
+	  /* finally, if there are authors AFTER our author, copy those */
+	  if(author_len+nmstart+idx < authors_len) {
+	    strncpy(thread->nonmatched_authors + idx - 2,", ",2);
+	    strncpy(thread->nonmatched_authors + idx, authorscopy+nmstart + idx + author_len + 2, 
+		    authors_len - (nmstart + idx + author_len + 2));
+	  }
+	}
+	free(authorscopy);
+    } else {
+	thread->nonmatched_authors += author_len;
+    }
+    return;
+}
+
+/* clean up the uggly "Lastname, Firstname" format to be "Firstname Lastname" 
+ */
+char *
+_thread_cleanup_author (notmuch_thread_t *thread,
+			const char *author)
+{
+  char *cleanauthor;
+  const char *comma;
+  int fname,lname;
+
+  cleanauthor = talloc_strdup(thread, author);
+  if (cleanauthor == NULL)
+    return NULL;
+  comma = strchr(author,',');
+  if (comma) {
+    lname = comma - author;
+    fname = strlen(author) - lname - 2;
+    strncpy(cleanauthor, comma + 2, fname);
+    *(cleanauthor+fname) = ' ';
+    strncpy(cleanauthor + fname + 1, author, lname);
+    *(cleanauthor+fname+1+lname) = '\0';
+  }
+  return cleanauthor;
 }
 
 /* Add 'message' as a message that belongs to 'thread'.
@@ -87,6 +175,7 @@ _thread_add_message (notmuch_thread_t *thread,
     InternetAddressList *list;
     InternetAddress *address;
     const char *from, *author;
+    char *cleanauthor;
 
     _notmuch_message_list_add_message (thread->message_list,
 				       talloc_steal (thread, message));
@@ -107,7 +196,9 @@ _thread_add_message (notmuch_thread_t *thread,
 		mailbox = INTERNET_ADDRESS_MAILBOX (address);
 		author = internet_address_mailbox_get_addr (mailbox);
 	    }
-	    _thread_add_author (thread, author);
+	    cleanauthor = _thread_cleanup_author (thread, author);
+	    _thread_add_author (thread, cleanauthor );
+	    notmuch_message_set_author (message, cleanauthor);
 	}
 	g_object_unref (G_OBJECT (list));
     }
@@ -150,6 +241,7 @@ _thread_add_matched_message (notmuch_thread_t *thread,
 	notmuch_message_set_flag (hashed_message,
 				  NOTMUCH_MESSAGE_FLAG_MATCH, 1);
     }
+    _thread_move_matched_author (thread,notmuch_message_get_author(hashed_message));
 }
 
 static void
@@ -251,6 +343,7 @@ _notmuch_thread_create (void *ctx,
     thread->authors_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
 						  free, NULL);
     thread->authors = NULL;
+    thread->nonmatched_authors = NULL;
     thread->tags = g_hash_table_new_full (g_str_hash, g_str_equal,
 					  free, NULL);
 


-- 
Dirk Hohndel
Intel Open Source Technology Center


More information about the notmuch mailing list