diff --git a/notmuch-private.h b/notmuch-private.h index c6d8e355..98ad4e26 100644 --- a/notmuch-private.h +++ b/notmuch-private.h @@ -144,6 +144,9 @@ _notmuch_thread_create (const void *talloc_owner, notmuch_database_t *notmuch, const char *thread_id); +void +_notmuch_thread_add_tag (notmuch_thread_t *thread, const char *tag); + /* message.cc */ notmuch_message_t * diff --git a/notmuch.c b/notmuch.c index cc53d1d6..bfcc2f6a 100644 --- a/notmuch.c +++ b/notmuch.c @@ -610,6 +610,7 @@ search_command (int argc, char *argv[]) notmuch_query_t *query; notmuch_thread_results_t *results; notmuch_thread_t *thread; + notmuch_tags_t *tags; char *query_str; int i; notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; @@ -641,9 +642,23 @@ search_command (int argc, char *argv[]) notmuch_thread_results_has_more (results); notmuch_thread_results_advance (results)) { + int first = 1; + thread = notmuch_thread_results_get (results); - printf ("%s\n", notmuch_thread_get_thread_id (thread)); + printf ("%s (", notmuch_thread_get_thread_id (thread)); + + for (tags = notmuch_thread_get_tags (thread); + notmuch_tags_has_more (tags); + notmuch_tags_advance (tags)) + { + if (! first) + printf (" "); + printf ("%s", notmuch_tags_get (tags)); + first = 0; + } + + printf (")\n"); notmuch_thread_destroy (thread); } diff --git a/notmuch.h b/notmuch.h index 522bf1b9..b4ff53e3 100644 --- a/notmuch.h +++ b/notmuch.h @@ -458,6 +458,45 @@ notmuch_thread_results_destroy (notmuch_thread_results_t *results); const char * notmuch_thread_get_thread_id (notmuch_thread_t *thread); +/* Get the tags for 'thread', returning a notmuch_tags_t object which + * can be used to iterate over all tags. + * + * Note: In the Notmuch database, tags are stored on individual + * messages, not on threads. So the tags returned here will be all + * tags of the messages which matched the search and which belong to + * this thread. + * + * The tags object is owned by the thread and as such, will only be + * valid for as long as the thread is valid, (for example, until + * notmuch_thread_destroy or until the query from which it derived is + * destroyed). + * + * Typical usage might be: + * + * notmuch_thread_t *thread; + * notmuch_tags_t *tags; + * const char *tag; + * + * thread = notmuch_thread_results_get (thread_results); + * + * for (tags = notmuch_thread_get_tags (thread); + * notmuch_tags_has_more (tags); + * notmuch_result_advance (tags)) + * { + * tag = notmuch_tags_get (tags); + * .... + * } + * + * notmuch_thread_destroy (thread); + * + * Note that there's no explicit destructor needed for the + * notmuch_tags_t object. (For consistency, we do provide a + * notmuch_tags_destroy function, but there's no good reason to call + * it if the message is about to be destroyed). + */ +notmuch_tags_t * +notmuch_thread_get_tags (notmuch_thread_t *thread); + /* Destroy a notmuch_thread_t object. */ void notmuch_thread_destroy (notmuch_thread_t *thread); diff --git a/query.cc b/query.cc index 4df2f52f..c8a91491 100644 --- a/query.cc +++ b/query.cc @@ -39,7 +39,7 @@ struct _notmuch_message_results { struct _notmuch_thread_results { notmuch_database_t *notmuch; - GPtrArray *thread_ids; + GPtrArray *threads; unsigned int index; }; @@ -165,7 +165,7 @@ notmuch_query_search_messages (notmuch_query_t *query) static int _notmuch_thread_results_destructor (notmuch_thread_results_t *results) { - g_ptr_array_free (results->thread_ids, TRUE); + g_ptr_array_free (results->threads, TRUE); return 0; } @@ -174,9 +174,12 @@ notmuch_thread_results_t * notmuch_query_search_threads (notmuch_query_t *query) { notmuch_thread_results_t *thread_results; + notmuch_thread_t *thread; + const char *thread_id; notmuch_message_results_t *message_results; notmuch_message_t *message; - const char *thread_id; + notmuch_tags_t *tags; + const char *tag; GHashTable *seen; thread_results = talloc (query, notmuch_thread_results_t); @@ -184,7 +187,7 @@ notmuch_query_search_threads (notmuch_query_t *query) return NULL; thread_results->notmuch = query->notmuch; - thread_results->thread_ids = g_ptr_array_new (); + thread_results->threads = g_ptr_array_new (); thread_results->index = 0; talloc_set_destructor (thread_results, _notmuch_thread_results_destructor); @@ -197,18 +200,28 @@ notmuch_query_search_threads (notmuch_query_t *query) notmuch_message_results_advance (message_results)) { message = notmuch_message_results_get (message_results); + thread_id = notmuch_message_get_thread_id (message); - if (g_hash_table_lookup_extended (seen, - thread_id, NULL, NULL)) + if (! g_hash_table_lookup_extended (seen, + thread_id, NULL, + (void **) &thread)) { - continue; + thread = _notmuch_thread_create (query, query->notmuch, + thread_id); + + g_hash_table_insert (seen, xstrdup (thread_id), thread); + + g_ptr_array_add (thread_results->threads, thread); } - g_hash_table_insert (seen, xstrdup (thread_id), NULL); - - g_ptr_array_add (thread_results->thread_ids, - talloc_strdup (thread_results, thread_id)); + for (tags = notmuch_message_get_tags (message); + notmuch_tags_has_more (tags); + notmuch_tags_advance (tags)) + { + tag = notmuch_tags_get (tags); + _notmuch_thread_add_tag (thread, tag); + } } g_hash_table_unref (seen); @@ -268,26 +281,17 @@ notmuch_message_results_destroy (notmuch_message_results_t *results) notmuch_bool_t notmuch_thread_results_has_more (notmuch_thread_results_t *results) { - return (results->index < results->thread_ids->len); + return (results->index < results->threads->len); } notmuch_thread_t * notmuch_thread_results_get (notmuch_thread_results_t *results) { - notmuch_thread_t *thread; - const char *thread_id; - if (! notmuch_thread_results_has_more (results)) return NULL; - thread_id = (const char *) g_ptr_array_index (results->thread_ids, - results->index); - - thread = _notmuch_thread_create (results, - results->notmuch, - thread_id); - - return thread; + return (notmuch_thread_t *) g_ptr_array_index (results->threads, + results->index); } void diff --git a/thread.cc b/thread.cc index 89157299..73d17722 100644 --- a/thread.cc +++ b/thread.cc @@ -23,11 +23,22 @@ #include +#include /* GHashTable */ + struct _notmuch_thread { notmuch_database_t *notmuch; char *thread_id; + GHashTable *tags; }; +static int +_notmuch_thread_destructor (notmuch_thread_t *thread) +{ + g_hash_table_unref (thread->tags); + + return 0; +} + /* Create a new notmuch_thread_t object for an existing document in * the database. * @@ -54,8 +65,12 @@ _notmuch_thread_create (const void *talloc_owner, if (unlikely (thread == NULL)) return NULL; + talloc_set_destructor (thread, _notmuch_thread_destructor); + thread->notmuch = notmuch; thread->thread_id = talloc_strdup (thread, thread_id); + thread->tags = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); return thread; } @@ -66,6 +81,34 @@ notmuch_thread_get_thread_id (notmuch_thread_t *thread) return thread->thread_id; } +void +_notmuch_thread_add_tag (notmuch_thread_t *thread, const char *tag) +{ + g_hash_table_insert (thread->tags, xstrdup (tag), NULL); +} + +notmuch_tags_t * +notmuch_thread_get_tags (notmuch_thread_t *thread) +{ + notmuch_tags_t *tags; + GList *keys, *l; + + tags = _notmuch_tags_create (thread); + if (unlikely (tags == NULL)) + return NULL; + + keys = g_hash_table_get_keys (thread->tags); + + for (l = keys; l; l = l->next) + _notmuch_tags_add_tag (tags, (char *) l->data); + + g_list_free (keys); + + _notmuch_tags_prepare_iterator (tags); + + return tags; +} + void notmuch_thread_destroy (notmuch_thread_t *thread) {