Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 9B2234196F0 for ; Tue, 6 Apr 2010 22:52:31 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -4.2 X-Spam-Level: X-Spam-Status: No, score=-4.2 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3] autolearn=ham Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id F21i+NaAOsCW for ; Tue, 6 Apr 2010 22:52:30 -0700 (PDT) Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) by olra.theworths.org (Postfix) with ESMTP id 1BC29431FC1 for ; Tue, 6 Apr 2010 22:52:30 -0700 (PDT) Received: from localhost ([::1] helo=localhost.localdomain) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1NzOBh-0003IW-Ep for notmuch@notmuchmail.org; Wed, 07 Apr 2010 05:52:29 +0000 Received: by localhost.localdomain (Postfix, from userid 500) id D52BCC007F; Tue, 6 Apr 2010 22:52:28 -0700 (PDT) From: Dirk Hohndel To: notmuch Subject: [RFC] reordering and cleanup of thread authors Date: Tue, 06 Apr 2010 22:52:28 -0700 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-SRS-Rewrite: SMTP reverse-path rewritten from by bombadil.infradead.org See http://www.infradead.org/rpr.html X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 07 Apr 2010 05:52:31 -0000 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