--- /dev/null
+Return-Path: <amdragon@mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 780CA429E21\r
+ for <notmuch@notmuchmail.org>; Wed, 11 Jan 2012 10:48:35 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+ tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id NvGxMEIV7LhK for <notmuch@notmuchmail.org>;\r
+ Wed, 11 Jan 2012 10:48:34 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-6.mit.edu (DMZ-MAILSEC-SCANNER-6.MIT.EDU\r
+ [18.7.68.35])\r
+ by olra.theworths.org (Postfix) with ESMTP id 73D8D431FB6\r
+ for <notmuch@notmuchmail.org>; Wed, 11 Jan 2012 10:48:34 -0800 (PST)\r
+X-AuditID: 12074423-b7f9c6d0000008c3-a7-4f0dd9824e6c\r
+Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
+ by dmz-mailsec-scanner-6.mit.edu (Symantec Messaging Gateway) with SMTP\r
+ id 42.EC.02243.289DD0F4; Wed, 11 Jan 2012 13:48:34 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+ by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q0BImXt0013787; \r
+ Wed, 11 Jan 2012 13:48:34 -0500\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+ (authenticated bits=0)\r
+ (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+ by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q0BImWho011482\r
+ (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+ Wed, 11 Jan 2012 13:48:33 -0500 (EST)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
+ (envelope-from <amdragon@mit.edu>)\r
+ id 1Rl3Dy-0004ET-Gz; Wed, 11 Jan 2012 13:48:38 -0500\r
+Date: Wed, 11 Jan 2012 13:48:38 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: Jani Nikula <jani@nikula.org>\r
+Subject: Re: [PATCH 2/3] lib: Add support for automatically excluding tags\r
+ from queries\r
+Message-ID: <20120111184838.GT20796@mit.edu>\r
+References: <20120109043101.GH20796@mit.edu>\r
+ <1326258173-21163-1-git-send-email-amdragon@mit.edu>\r
+ <1326258173-21163-3-git-send-email-amdragon@mit.edu>\r
+ <87ty42v99m.fsf@nikula.org>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=us-ascii\r
+Content-Disposition: inline\r
+In-Reply-To: <87ty42v99m.fsf@nikula.org>\r
+User-Agent: Mutt/1.5.21 (2010-09-15)\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFmphleLIzCtJLcpLzFFi42IRYrdT1226yetv8G0Fu0XTdGeL6zdnMjsw\r
+ edy6/5rd49mqW8wBTFFcNimpOZllqUX6dglcGTsWnGcqWGBYcfVdJ1MD41m1LkZODgkBE4n1\r
+ 7/pYIGwxiQv31rN1MXJxCAnsY5RY19vICOFsYJR49vUlVOYkk8SRd49ZIJwljBJzXj0D62cR\r
+ UJXYPekuG4jNJqAhsW3/ckYQW0RAUWLzyf1gNrOAtMS3381MILawQKTEh01/2UFsXgEdidub\r
+ dkGt28EoceXKVWaIhKDEyZlPWCCatSRu/HsJ1MwBNmj5Pw6QMCfQrr2TF4LNERVQkZhychvb\r
+ BEahWUi6ZyHpnoXQvYCReRWjbEpulW5uYmZOcWqybnFyYl5eapGumV5uZoleakrpJkZwYLso\r
+ 72D8c1DpEKMAB6MSD++Ovbz+QqyJZcWVuYcYJTmYlER5V90ACvEl5adUZiQWZ8QXleakFh9i\r
+ lOBgVhLhdaoByvGmJFZWpRblw6SkOViUxHk1tN75CQmkJ5akZqemFqQWwWRlODiUJHg3ggwV\r
+ LEpNT61Iy8wpQUgzcXCCDOcBGr4cpIa3uCAxtzgzHSJ/ilFRSpx3EkhCACSRUZoH1wtLPK8Y\r
+ xYFeEebNB6niASYtuO5XQIOZgAZvWccDMrgkESEl1cAo8e7tdu6TH2te1ITEXm/eF/uAKyQ3\r
+ gOPh1puFVy/n3NyzJnnZ0fcf6wt0zW5GrP7SqdrT4L//+f/glCXPJCOC3i8yfJC/96VjY2JK\r
+ xt+sOWd2njt7RU5Rg/PGEg1GtZtxMStidoQt8bb9UVFb0bDz2vfuR+vWOrPILWzw12r9wrdk\r
+ gVbM64W/lFiKMxINtZiLihMBpsQZ9BcDAAA=\r
+Cc: notmuch@notmuchmail.org\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 11 Jan 2012 18:48:35 -0000\r
+\r
+Quoth Jani Nikula on Jan 11 at 10:11 am:\r
+> On Wed, 11 Jan 2012 00:02:52 -0500, Austin Clements <amdragon@MIT.EDU> wrote:\r
+> > This is useful for tags like "deleted" and "spam" that people\r
+> > generally want to exclude from query results. These exclusions will\r
+> > be overridden if a tag is explicitly mentioned in a query.\r
+> > ---\r
+> > lib/notmuch.h | 6 ++++++\r
+> > lib/query.cc | 33 +++++++++++++++++++++++++++++++++\r
+> > 2 files changed, 39 insertions(+), 0 deletions(-)\r
+> > \r
+> > diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+> > index 9f23a10..0a3ae2b 100644\r
+> > --- a/lib/notmuch.h\r
+> > +++ b/lib/notmuch.h\r
+> > @@ -457,6 +457,12 @@ notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort);\r
+> > notmuch_sort_t\r
+> > notmuch_query_get_sort (notmuch_query_t *query);\r
+> > \r
+> > +/* Add a tag that will be excluded by default from the query results.\r
+> > + * This exclusion will be overridden if this tag appears explicitly in\r
+> > + * the query. */\r
+> > +void\r
+> > +notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag);\r
+> > +\r
+> > /* Execute a query for threads, returning a notmuch_threads_t object\r
+> > * which can be used to iterate over the results. The returned threads\r
+> > * object is owned by the query and as such, will only be valid until\r
+> > diff --git a/lib/query.cc b/lib/query.cc\r
+> > index b6c0f12..716db1c 100644\r
+> > --- a/lib/query.cc\r
+> > +++ b/lib/query.cc\r
+> > @@ -27,6 +27,7 @@ struct _notmuch_query {\r
+> > notmuch_database_t *notmuch;\r
+> > const char *query_string;\r
+> > notmuch_sort_t sort;\r
+> > + notmuch_string_list_t *exclude_terms;\r
+> > };\r
+> > \r
+> > typedef struct _notmuch_mset_messages {\r
+> > @@ -76,6 +77,8 @@ notmuch_query_create (notmuch_database_t *notmuch,\r
+> > \r
+> > query->sort = NOTMUCH_SORT_NEWEST_FIRST;\r
+> > \r
+> > + query->exclude_terms = _notmuch_string_list_create (query);\r
+> > +\r
+> > return query;\r
+> > }\r
+> > \r
+> > @@ -97,6 +100,13 @@ notmuch_query_get_sort (notmuch_query_t *query)\r
+> > return query->sort;\r
+> > }\r
+> > \r
+> > +void\r
+> > +notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag)\r
+> > +{\r
+> > + char *term = talloc_asprintf (query, "%s%s", _find_prefix ("tag"), tag);\r
+> > + _notmuch_string_list_append (query->exclude_terms, term);\r
+> > +}\r
+> > +\r
+> > /* We end up having to call the destructors explicitly because we had\r
+> > * to use "placement new" in order to initialize C++ objects within a\r
+> > * block that we allocated with talloc. So C++ is making talloc\r
+> > @@ -112,6 +122,25 @@ _notmuch_messages_destructor (notmuch_mset_messages_t *messages)\r
+> > return 0;\r
+> > }\r
+> > \r
+> \r
+> I'd like to have a comment here, or inline in the code, explaining the\r
+> following function a little bit.\r
+\r
+Done.\r
+\r
+> > +static Xapian::Query\r
+> > +_notmuch_exclude_tags (notmuch_query_t *query, Xapian::Query xquery)\r
+> > +{\r
+> > + Xapian::TermIterator end = xquery.get_terms_end ();\r
+> > +\r
+> > + for (notmuch_string_node_t *term = query->exclude_terms->head; term;\r
+> > + term = term->next) {\r
+> > + Xapian::TermIterator it = xquery.get_terms_begin ();\r
+> > + for (; it != end; it++) {\r
+> > + if (*it == term->string)\r
+> \r
+> [This is a double reminder to me why I'm not that enthusiastic about\r
+> operator overloading in C++.]\r
+\r
+Actually, that's a good point. I had originally done this to prevent\r
+std::string from performing any internal allocation or copying (which\r
+calling c_str could do), but since it took me some digging to figure\r
+out if I had *actually* avoided this (turns out there's an overloaded\r
+string==char*, so I had), I've switched to using string.compare, which\r
+I think makes the behavior and performance more obvious.\r
+\r
+> > + break;\r
+> > + }\r
+> > + if (it == end)\r
+> > + xquery = Xapian::Query (Xapian::Query::OP_AND_NOT,\r
+> > + xquery, Xapian::Query (term->string));\r
+> \r
+> I briefly dug into Xapian documentation and source code, and became none\r
+> the wiser whether this copies the queries passed to it or not, i.e. does\r
+> this leak memory or not. I just presume you know what you're doing. ;)\r
+\r
+Since my code isn't doing any memory allocation, it can't leak memory.\r
+It could do a lot of copying, but it turns out that isn't a problem\r
+either because Xapian objects are internally reference-counted\r
+handles. So, in other words, if you write the obvious thing, it will\r
+do the right thing, which is totally contrary to what C++ has\r
+conditioned us to expect. ]:--8)\r
+\r
+> I think the function fails if someone is stupid enough to exclude the\r
+> same tag twice. I'm not sure if you should care. If you think so, you\r
+> could just check double add in notmuch_query_add_tag_exclude().\r
+\r
+It handles this correctly for two reasons. Suppose tag x is excluded\r
+twice. At worst, if tag:x doesn't appear in the query, the returned\r
+query will get two AND NOT tag:x's, but that doesn't affect the\r
+results. It turns out it doesn't even get doubled up for a slightly\r
+subtle reason: when the loop reaches the second x in the exclude list,\r
+it will iterate over the new query, which already has the AND NOT\r
+tag:x in it, so it will see the tag:x as if it were in the original\r
+query and not further modify the query.\r
+\r
+However, this did point out a bug. I was using the end iterator from\r
+the original query even when I started iterating over the modified\r
+query.\r
+\r
+I'll send out an updated series after someone looks at the third\r
+patch.\r
+\r
+> Otherwise, looks good.\r
+> \r
+> > + }\r
+> > + return xquery;\r
+> > +}\r
+> > +\r
+> > notmuch_messages_t *\r
+> > notmuch_query_search_messages (notmuch_query_t *query)\r
+> > {\r
+> > @@ -157,6 +186,8 @@ notmuch_query_search_messages (notmuch_query_t *query)\r
+> > mail_query, string_query);\r
+> > }\r
+> > \r
+> > + final_query = _notmuch_exclude_tags (query, final_query);\r
+> > +\r
+> > enquire.set_weighting_scheme (Xapian::BoolWeight());\r
+> > \r
+> > switch (query->sort) {\r
+> > @@ -436,6 +467,8 @@ notmuch_query_count_messages (notmuch_query_t *query)\r
+> > mail_query, string_query);\r
+> > }\r
+> > \r
+> > + final_query = _notmuch_exclude_tags (query, final_query);\r
+> > +\r
+> > enquire.set_weighting_scheme(Xapian::BoolWeight());\r
+> > enquire.set_docid_order(Xapian::Enquire::ASCENDING);\r
+> > \r
+> \r