]> git.notmuchmail.org Git - notmuch/commitdiff
emacs: Add new option notmuch-search-hide-excluded master
authorMohsin Kaleem <mohkale@kisara.moe>
Sun, 10 Mar 2024 18:57:41 +0000 (18:57 +0000)
committerDavid Bremner <david@tethera.net>
Sat, 6 Apr 2024 18:03:45 +0000 (15:03 -0300)
The new notmuch-search-hide-excluded option allows users to configure whether
to show or hide excluded messages (as determined by search.exclude_tags
in the local notmuch config file). It defaults to true for now to maintain
backwards-compatibility with how notmuch-{search,tree} already worked.

New commands notmuch-search-toggle-hide-excluded and
notmuch-tree-toggle-exclude have also been added. They toggle the value
of notmuch-search-hide-excluded for the search in the current search or
tree buffer. It's bound to "i" in the respective keymaps for these
modes.

Lastly I've amended some calls to notmuch-tree and notmuch-unthreaded
which didn't pass through the buffer local value of
notmuch-search-oldest-first (and now notmuch-search-exclude).
Examples of where I've done this include:
  + notmuch-jump-search
  + notmuch-tree-from-search-current-query
  + notmuch-unthreaded-from-search-current-query
  + notmuch-tree-from-search-thread

A new test file for Emacs has been added which covers the usage of the
new `notmuch-search-hide-excluded' option and interactively hiding or
showing mail with excluded tags. These test cover the basic usage of
the `notmuch-search-toggle-hide-excluded' command in notmuch-search,
notmuch-tree and notmuch-unthreaded searches. These tests also cover
the persistence of the current value of the hide-excluded mail option
as a user switches from between these different search commands.

[1]: id:87ilxlxsng.fsf@kisara.moe

Amended-by: db, fix indentation in T461-emacs-search-exclude.sh
140 files changed:
.gitignore
NEWS
bindings/Makefile.local
bindings/python/notmuch/compat.py
bindings/python/notmuch/version.py
bindings/ruby/database.c
bindings/ruby/defs.h
bindings/ruby/directory.c
bindings/ruby/filenames.c
bindings/ruby/init.c
bindings/ruby/message.c
bindings/ruby/messages.c
bindings/ruby/query.c
bindings/ruby/tags.c
bindings/ruby/thread.c
compat/have_strcasestr.c
completion/notmuch-completion.bash
completion/zsh/_notmuch
configure
contrib/notmuch-mutt/README
contrib/notmuch-mutt/notmuch-mutt
debian/changelog
debian/control
debian/libnotmuch5.install [deleted file]
debian/libnotmuch5.symbols [deleted file]
debian/libnotmuch5t64.install [new file with mode: 0644]
debian/libnotmuch5t64.lintian-overrides [new file with mode: 0644]
debian/libnotmuch5t64.symbols [new file with mode: 0644]
debian/rules
debian/tests/control [new file with mode: 0644]
devel/emacs-keybindings.org
devel/notmuch-web/nmweb.py
devel/notmuch-web/templates/show.html
devel/schemata
doc/Makefile.local
doc/conf.py
doc/man1/notmuch-config.rst
doc/man1/notmuch-git.rst
doc/man1/notmuch-search.rst
doc/man1/notmuch-show.rst
doc/man7/notmuch-sexp-queries.rst
doc/notmuch-emacs.rst
emacs/notmuch-address.el
emacs/notmuch-emacs-mua
emacs/notmuch-hello.el
emacs/notmuch-jump.el
emacs/notmuch-lib.el
emacs/notmuch-mua.el
emacs/notmuch-parser.el
emacs/notmuch-print.el
emacs/notmuch-show.el
emacs/notmuch-tag.el
emacs/notmuch-tree.el
emacs/notmuch-wash.el
emacs/notmuch.el
lib/config.cc
lib/database-private.h
lib/database.cc
lib/index.cc
lib/message-property.cc
lib/message.cc
lib/notmuch-private.h
lib/notmuch.h
lib/open.cc
lib/query.cc
lib/regexp-fields.cc
lib/string-map.c
notmuch-client.h
notmuch-config.c
notmuch-git.py
notmuch-new.c
notmuch-show.c
notmuch.c
performance-test/README
performance-test/T02-tag.sh
performance-test/download/notmuch-email-corpus-0.5.tar.xz.asc [new file with mode: 0644]
performance-test/version.sh
test/README
test/T000-basic.sh
test/T010-help-test.sh
test/T030-config.sh
test/T040-setup.sh
test/T050-new.sh
test/T055-path-config.sh
test/T060-count.sh
test/T080-search.sh
test/T081-sexpr-search.sh
test/T095-address.sh
test/T131-show-limiting.sh [new file with mode: 0755]
test/T150-tagging.sh
test/T160-json.sh
test/T220-reply.sh
test/T240-dump-restore.sh
test/T310-emacs.sh
test/T350-crypto.sh
test/T355-smime.sh
test/T360-symbol-hiding.sh
test/T390-python.sh
test/T391-python-cffi.sh
test/T392-python-cffi-notmuch.sh
test/T395-ruby.sh
test/T410-argument-parsing.sh
test/T460-emacs-tree.sh
test/T461-emacs-search-exclude.sh [new file with mode: 0755]
test/T480-hex-escaping.sh
test/T490-parse-time-string.sh
test/T520-show.sh
test/T550-db-features.sh
test/T566-lib-message.sh
test/T570-revision-tracking.sh
test/T590-libconfig.sh
test/T592-thread-breakage.sh
test/T610-message-property.sh
test/T700-reindex.sh
test/T710-message-id.sh
test/T760-as-text.sh [new file with mode: 0755]
test/T800-asan.sh
test/T810-tsan.sh [new file with mode: 0755]
test/T810-tsan.suppressions [new file with mode: 0644]
test/T850-git.sh
test/corpora/indexing/fake-pdf:2,S [new file with mode: 0644]
test/emacs-exclude.expected-output/notmuch-search-tag-inbox-with-excluded [new file with mode: 0644]
test/emacs-exclude.expected-output/notmuch-search-tag-inbox-without-excluded [new file with mode: 0644]
test/emacs-exclude.expected-output/notmuch-tree-tag-inbox-with-excluded [new file with mode: 0644]
test/emacs-exclude.expected-output/notmuch-tree-tag-inbox-without-excluded [new file with mode: 0644]
test/emacs-exclude.expected-output/notmuch-unthreaded-tag-inbox-with-excluded [new file with mode: 0644]
test/emacs-exclude.expected-output/notmuch-unthreaded-tag-inbox-without-excluded [new file with mode: 0644]
test/emacs-tree.expected-output/inbox-outline [new file with mode: 0644]
test/emacs-tree.expected-output/notmuch-tree-tag-inbox-oldest-first [new file with mode: 0644]
test/export-dirs.sh
test/notmuch-test
test/setup.expected-output/config-with-comments
test/test-lib-common.sh
test/test-lib-emacs.sh
test/test-lib.el
test/test-lib.sh
util/xapian-extra.h [new file with mode: 0644]
version.txt
vim/README
vim/notmuch.txt

index f94d1480c1f91d5ff64e8a0f746f4f79de19b839..eda6d9cff7473613aca1e36d54fb4dd5e8872a2c 100644 (file)
@@ -21,3 +21,4 @@
 /bindings/python-cffi/_notmuch_config.py
 TAGS
 tags
+__pycache__
diff --git a/NEWS b/NEWS
index b83551f1f132c812e9fc888ca7fb90d57eee666a..cf8107f26362431549dc410cb8379de5c10221f1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,119 @@
+Notmuch 0.38.3 (2024-03-09)
+===========================
+
+CLI
+---
+
+Fix a bug in configuration code that caused the notmuch command to
+erroneously report "Error: could not locate database" under some
+circumstances.
+
+Notmuch 0.38.2 (2023-12-01)
+===========================
+
+Library
+-------
+
+Make sorting of string maps lexicographic on (key,value) pairs. This
+avoids some test failures due to variation in message property output
+order.
+
+Emacs
+-----
+
+Avoid extra separators after the last address in `notmuch-emacs-mua`.
+
+
+Notmuch 0.38.1 (2023-10-26)
+===========================
+
+CLI
+---
+
+Report parse errors in config files.
+
+Emacs
+-----
+
+Fix image toggling for Emacs >= 29.1.
+
+notmuch-mutt
+------------
+
+Fix syntax error in script.
+
+Notmuch 0.38 (2023-09-12)
+=========================
+
+General
+-------
+
+Support relative lastmod queries (see notmuch-sexp-queries(7) and
+notmuch-search-terms(7) for details).
+
+Support indexing of designated attachments as text (see
+notmuch-config(1) for details).
+
+CLI
+---
+
+Add options --offset and --limit to notmuch-show(1).
+
+Emacs
+-----
+
+New commands notmuch-search-edit-search and notmuch-tree-edit-search.
+
+Introduce notmuch-tree-outline-mode.
+
+Some compatibility fixes for Emacs 29. At least one issue (hiding
+images) remains in 0.38.
+
+Support completion when piping to external command.
+
+Fix regression in updating tag display introduced by 0.37.
+
+Library
+-------
+
+Fix bug creating database when database.path is not set.
+
+Incremental performance improvements for message deletion.
+
+Catch Xapian exceptions when deleting messages.
+
+Sync removed message properties to the database.
+
+Replace use of thread-unsafe Query::MatchAll in the infix query
+parser.
+
+Notmuch-Mutt
+------------
+
+Be more careful when clearing the results directory.
+
+Ruby
+----
+
+Use `database_open_with_config`, and provide compatible path search
+semantics.
+
+Bugfix for query.get_sort
+
+Test Suite
+----------
+
+Support testing installed version of notmuch.
+
+Adapt to some breaking changes in glib handling of init files.
+
+Replace OpenPGP key used in test suite.
+
+Performance Tests
+-----------------
+
+Update signatures for performance test corpus.
+
 Notmuch 0.37 (2022-08-21)
 =========================
 
@@ -54,7 +170,7 @@ Library
 
 Add the `sexp` prefix to the infix (traditional) query parser. This
 allows specific subqueries to be parsed by the sexp parser (with
-appropropriate quoting). See `notmuch-search-terms(7)` for details.
+appropriate quoting). See `notmuch-search-terms(7)` for details.
 
 Add another heuristic to regexp fields to prevent phrase parsing of
 bracketed sub-expressions.
@@ -76,7 +192,7 @@ the database.
 Documentation
 -------------
 
-Reorganize documention for `notmuch-config`. Add a few links from
+Reorganize documentation for `notmuch-config`. Add a few links from
 other man pages.
 
 Emacs
@@ -123,7 +239,7 @@ Fix some small memory leaks in `notmuch_database_open_with_config`.
 CLI
 ---
 
-Improve handling of leading/trailing punctation and space for
+Improve handling of leading/trailing punctuation and space for
 configuration lists.
 
 Only ignore `.notmuch` at the top level in `notmuch new`.
@@ -142,7 +258,7 @@ lazily.
 Don't add space to completion candidates, improves compatibility with
 third party completion frameworks.
 
-Make citation formating more robust against whitespace.
+Make citation formatting more robust against whitespace.
 
 Use `--excludes=false` when generating the 'All tags' section.
 
@@ -543,7 +659,7 @@ Notmuch 0.31.2 (2020-11-08)
 Build
 -----
 
-Catch one more occurence of "version" in the build system, which
+Catch one more occurrence of "version" in the build system, which
 caused the file to be regenerated in the release tarball.
 
 Notmuch 0.31.1 (2020-11-08)
index 7b10af088abbb98716ae45c4cba35cf7a50ffeb2..9875123ab106cc6a94f5d62ba4e29ea60f24e742 100644 (file)
@@ -34,3 +34,7 @@ CLEAN += $(patsubst %,$(dir)/ruby/%, \
 CLEAN += bindings/ruby/.vendorarchdir.time $(dir)/ruby.stamp
 
 CLEAN += bindings/python-cffi/build $(dir)/python-cffi.stamp
+CLEAN += bindings/python-cffi/__pycache__
+
+DISTCLEAN += bindings/python-cffi/_notmuch_config.py \
+       bindings/python-cffi/notmuch2.egg-info
index c931329e96972237d5f380ba2270a9dcc2d28d56..4a94e05c805d1a9fc28d035fbde09f3b404ff202 100644 (file)
@@ -47,7 +47,10 @@ if sys.version_info[0] == 2:
 
         return value
 else:
-    from configparser import SafeConfigParser
+    from configparser import ConfigParser as SafeConfigParser
+
+    if not hasattr(SafeConfigParser, 'readfp'):   # py >= 3.12
+        SafeConfigParser.readfp = SafeConfigParser.read_file
 
     class Python3StringMixIn(object):
         def __str__(self):
index 943f335cb7b5b011ffc7c7d12b808985006dcc82..fd4152eef9ca772fff115b70860f8fb506c21ab0 100644 (file)
@@ -1,3 +1,3 @@
 # this file should be kept in sync with ../../../version
-__VERSION__ = '0.37'
+__VERSION__ = '0.38.3'
 SOVERSION = '5'
index 9c3dbd96f0b292f856c2ccca2448b8483503a568..ed224ef713bf1f1ed2319678038be5182563d38b 100644 (file)
@@ -58,40 +58,56 @@ notmuch_rb_database_initialize (int argc, VALUE *argv, VALUE self)
     notmuch_database_t *database;
     notmuch_status_t ret;
 
+    path = NULL;
+    create = 0;
+    mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
+
     /* Check arguments */
-    rb_scan_args (argc, argv, "11", &pathv, &hashv);
+    rb_scan_args (argc, argv, "02", &pathv, &hashv);
 
-    SafeStringValue (pathv);
-    path = RSTRING_PTR (pathv);
+    if (!NIL_P (pathv)) {
+       SafeStringValue (pathv);
+       path = RSTRING_PTR (pathv);
+    }
 
     if (!NIL_P (hashv)) {
-       Check_Type (hashv, T_HASH);
-       create = RTEST (rb_hash_aref (hashv, ID2SYM (ID_db_create)));
-       modev = rb_hash_aref (hashv, ID2SYM (ID_db_mode));
-       if (NIL_P (modev))
-           mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
-       else if (!FIXNUM_P (modev))
-           rb_raise (rb_eTypeError, ":mode isn't a Fixnum");
-       else {
-           mode = FIX2INT (modev);
-           switch (mode) {
-           case NOTMUCH_DATABASE_MODE_READ_ONLY:
-           case NOTMUCH_DATABASE_MODE_READ_WRITE:
-               break;
-           default:
-               rb_raise ( rb_eTypeError, "Invalid mode");
+       VALUE rmode, rcreate;
+       VALUE kwargs[2];
+       static ID keyword_ids[2];
+
+       if (!keyword_ids[0]) {
+           keyword_ids[0] = rb_intern_const ("mode");
+           keyword_ids[1] = rb_intern_const ("create");
+       }
+
+       rb_get_kwargs (hashv, keyword_ids, 0, 2, kwargs);
+
+       rmode = kwargs[0];
+       rcreate = kwargs[1];
+
+       if (rmode != Qundef) {
+           if (!FIXNUM_P (rmode))
+               rb_raise (rb_eTypeError, ":mode isn't a Fixnum");
+           else {
+               mode = FIX2INT (rmode);
+               switch (mode) {
+               case NOTMUCH_DATABASE_MODE_READ_ONLY:
+               case NOTMUCH_DATABASE_MODE_READ_WRITE:
+                   break;
+               default:
+                   rb_raise ( rb_eTypeError, "Invalid mode");
+               }
            }
        }
-    } else {
-       create = 0;
-       mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
+       if (rcreate != Qundef)
+           create = RTEST (rcreate);
     }
 
     rb_check_typeddata (self, &notmuch_rb_database_type);
     if (create)
        ret = notmuch_database_create (path, &database);
     else
-       ret = notmuch_database_open (path, mode, &database);
+       ret = notmuch_database_open_with_config (path, mode, NULL, NULL, &database, NULL);
     notmuch_rb_status_raise (ret);
 
     DATA_PTR (self) = notmuch_rb_object_create (database, "notmuch_rb_database");
@@ -408,7 +424,7 @@ notmuch_rb_database_get_all_tags (VALUE self)
 
        rb_raise (notmuch_rb_eBaseError, "%s", msg);
     }
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cTags, &notmuch_rb_tags_type, tags);
+    return notmuch_rb_tags_get (tags);
 }
 
 /*
index e2541e8f8de30f6c828be3edbde0699d57760dcc..a2cb38c8a74425ed193144c2607b752210f603f5 100644 (file)
@@ -33,7 +33,6 @@ extern VALUE notmuch_rb_cThreads;
 extern VALUE notmuch_rb_cThread;
 extern VALUE notmuch_rb_cMessages;
 extern VALUE notmuch_rb_cMessage;
-extern VALUE notmuch_rb_cTags;
 
 extern VALUE notmuch_rb_eBaseError;
 extern VALUE notmuch_rb_eDatabaseError;
@@ -48,8 +47,6 @@ extern VALUE notmuch_rb_eUnbalancedFreezeThawError;
 extern VALUE notmuch_rb_eUnbalancedAtomicError;
 
 extern ID ID_call;
-extern ID ID_db_create;
-extern ID ID_db_mode;
 
 /* RSTRING_PTR() is new in ruby-1.9 */
 #if !defined(RSTRING_PTR)
@@ -59,7 +56,6 @@ extern ID ID_db_mode;
 extern const rb_data_type_t notmuch_rb_object_type;
 extern const rb_data_type_t notmuch_rb_database_type;
 extern const rb_data_type_t notmuch_rb_directory_type;
-extern const rb_data_type_t notmuch_rb_filenames_type;
 extern const rb_data_type_t notmuch_rb_query_type;
 extern const rb_data_type_t notmuch_rb_threads_type;
 extern const rb_data_type_t notmuch_rb_thread_type;
@@ -92,9 +88,6 @@ extern const rb_data_type_t notmuch_rb_tags_type;
 #define Data_Get_Notmuch_Directory(obj, ptr) \
     Data_Get_Notmuch_Object ((obj), &notmuch_rb_directory_type, (ptr))
 
-#define Data_Get_Notmuch_FileNames(obj, ptr) \
-    Data_Get_Notmuch_Object ((obj), &notmuch_rb_filenames_type, (ptr))
-
 #define Data_Get_Notmuch_Query(obj, ptr) \
     Data_Get_Notmuch_Object ((obj), &notmuch_rb_query_type, (ptr))
 
@@ -226,10 +219,7 @@ notmuch_rb_directory_get_child_directories (VALUE self);
 
 /* filenames.c */
 VALUE
-notmuch_rb_filenames_destroy (VALUE self);
-
-VALUE
-notmuch_rb_filenames_each (VALUE self);
+notmuch_rb_filenames_get (notmuch_filenames_t *fnames);
 
 /* query.c */
 VALUE
@@ -370,10 +360,7 @@ notmuch_rb_message_thaw (VALUE self);
 
 /* tags.c */
 VALUE
-notmuch_rb_tags_destroy (VALUE self);
-
-VALUE
-notmuch_rb_tags_each (VALUE self);
+notmuch_rb_tags_get (notmuch_tags_t *tags);
 
 /* init.c */
 void
index 910f0a99f238239f2c5717b2a85b2cbca190bcf5..f267d82f16976ee7fec41779c11d332ab7ac35ad 100644 (file)
@@ -87,7 +87,7 @@ notmuch_rb_directory_get_child_files (VALUE self)
 
     fnames = notmuch_directory_get_child_files (dir);
 
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cFileNames, &notmuch_rb_filenames_type, fnames);
+    return notmuch_rb_filenames_get (fnames);
 }
 
 /*
@@ -106,5 +106,5 @@ notmuch_rb_directory_get_child_directories (VALUE self)
 
     fnames = notmuch_directory_get_child_directories (dir);
 
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cFileNames, &notmuch_rb_filenames_type, fnames);
+    return notmuch_rb_filenames_get (fnames);
 }
index 0dec19520b59b0b421df5c7a4cb03826e69d235d..60c3fb8b3b412e121d5cea76b46b11af15b25191 100644 (file)
@@ -1,53 +1,11 @@
-/* The Ruby interface to the notmuch mail library
- *
- * Copyright © 2010 Ali Polatel
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see https://www.gnu.org/licenses/ .
- *
- * Author: Ali Polatel <alip@exherbo.org>
- */
-
 #include "defs.h"
 
-/*
- * call-seq: FILENAMES.destroy! => nil
- *
- * Destroys the filenames, freeing all resources allocated for it.
- */
-VALUE
-notmuch_rb_filenames_destroy (VALUE self)
-{
-    notmuch_rb_object_destroy (self, &notmuch_rb_filenames_type);
-
-    return Qnil;
-}
-
-/*
- * call-seq: FILENAMES.each {|item| block } => FILENAMES
- *
- * Calls +block+ once for each element in +self+, passing that element as a
- * parameter.
- */
 VALUE
-notmuch_rb_filenames_each (VALUE self)
+notmuch_rb_filenames_get (notmuch_filenames_t *fnames)
 {
-    notmuch_filenames_t *fnames;
-
-    Data_Get_Notmuch_FileNames (self, fnames);
+    VALUE rb_array = rb_ary_new ();
 
     for (; notmuch_filenames_valid (fnames); notmuch_filenames_move_to_next (fnames))
-       rb_yield (rb_str_new2 (notmuch_filenames_get (fnames)));
-
-    return self;
+       rb_ary_push (rb_array, rb_str_new2 (notmuch_filenames_get (fnames)));
+    return rb_array;
 }
index cd9f04cd734687da69d2a3b30a511c4f9a3f2c50..2d1994af22740c6640d91367b8d81b40283fe717 100644 (file)
 
 VALUE notmuch_rb_cDatabase;
 VALUE notmuch_rb_cDirectory;
-VALUE notmuch_rb_cFileNames;
 VALUE notmuch_rb_cQuery;
 VALUE notmuch_rb_cThreads;
 VALUE notmuch_rb_cThread;
 VALUE notmuch_rb_cMessages;
 VALUE notmuch_rb_cMessage;
-VALUE notmuch_rb_cTags;
 
 VALUE notmuch_rb_eBaseError;
 VALUE notmuch_rb_eDatabaseError;
@@ -43,8 +41,6 @@ VALUE notmuch_rb_eUnbalancedFreezeThawError;
 VALUE notmuch_rb_eUnbalancedAtomicError;
 
 ID ID_call;
-ID ID_db_create;
-ID ID_db_mode;
 
 const rb_data_type_t notmuch_rb_object_type = {
     .wrap_struct_name = "notmuch_object",
@@ -65,13 +61,11 @@ const rb_data_type_t notmuch_rb_object_type = {
 
 define_type (database);
 define_type (directory);
-define_type (filenames);
 define_type (query);
 define_type (threads);
 define_type (thread);
 define_type (messages);
 define_type (message);
-define_type (tags);
 
 /*
  * Document-module: Notmuch
@@ -86,13 +80,11 @@ define_type (tags);
  * the user:
  *
  * - Notmuch::Database
- * - Notmuch::FileNames
  * - Notmuch::Query
  * - Notmuch::Threads
  * - Notmuch::Messages
  * - Notmuch::Thread
  * - Notmuch::Message
- * - Notmuch::Tags
  */
 
 void
@@ -101,8 +93,6 @@ Init_notmuch (void)
     VALUE mod;
 
     ID_call = rb_intern ("call");
-    ID_db_create = rb_intern ("create");
-    ID_db_mode = rb_intern ("mode");
 
     mod = rb_define_module ("Notmuch");
 
@@ -297,17 +287,6 @@ Init_notmuch (void)
     rb_define_method (notmuch_rb_cDirectory, "child_files", notmuch_rb_directory_get_child_files, 0); /* in directory.c */
     rb_define_method (notmuch_rb_cDirectory, "child_directories", notmuch_rb_directory_get_child_directories, 0); /* in directory.c */
 
-    /*
-     * Document-class: Notmuch::FileNames
-     *
-     * Notmuch file names
-     */
-    notmuch_rb_cFileNames = rb_define_class_under (mod, "FileNames", rb_cObject);
-    rb_undef_method (notmuch_rb_cFileNames, "initialize");
-    rb_define_method (notmuch_rb_cFileNames, "destroy!", notmuch_rb_filenames_destroy, 0); /* in filenames.c */
-    rb_define_method (notmuch_rb_cFileNames, "each", notmuch_rb_filenames_each, 0); /* in filenames.c */
-    rb_include_module (notmuch_rb_cFileNames, rb_mEnumerable);
-
     /*
      * Document-class: Notmuch::Query
      *
@@ -395,15 +374,4 @@ Init_notmuch (void)
     rb_define_method (notmuch_rb_cMessage, "tags_to_maildir_flags", notmuch_rb_message_tags_to_maildir_flags, 0); /* in message.c */
     rb_define_method (notmuch_rb_cMessage, "freeze", notmuch_rb_message_freeze, 0); /* in message.c */
     rb_define_method (notmuch_rb_cMessage, "thaw", notmuch_rb_message_thaw, 0); /* in message.c */
-
-    /*
-     * Document-class: Notmuch::Tags
-     *
-     * Notmuch tags
-     */
-    notmuch_rb_cTags = rb_define_class_under (mod, "Tags", rb_cObject);
-    rb_undef_method (notmuch_rb_cTags, "initialize");
-    rb_define_method (notmuch_rb_cTags, "destroy!", notmuch_rb_tags_destroy, 0); /* in tags.c */
-    rb_define_method (notmuch_rb_cTags, "each", notmuch_rb_tags_each, 0); /* in tags.c */
-    rb_include_module (notmuch_rb_cTags, rb_mEnumerable);
 }
index f45c95cc5051d8475920c5a59bf07706e829e0be..13c182f6549481b0866fb1789e25e8d874ac8b69 100644 (file)
@@ -120,7 +120,7 @@ notmuch_rb_message_get_filenames (VALUE self)
 
     fnames = notmuch_message_get_filenames (message);
 
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cFileNames, &notmuch_rb_filenames_type, fnames);
+    return notmuch_rb_filenames_get (fnames);
 }
 
 /*
@@ -221,7 +221,7 @@ notmuch_rb_message_get_tags (VALUE self)
     if (!tags)
        rb_raise (notmuch_rb_eMemoryError, "Out of memory");
 
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cTags, &notmuch_rb_tags_type, tags);
+    return notmuch_rb_tags_get (tags);
 }
 
 /*
index ca5b10d04b09702629eddbeb1eb217bef84a4274..6369d0523008b6dadda38bc58cde3c8c908badf5 100644 (file)
@@ -71,5 +71,5 @@ notmuch_rb_messages_collect_tags (VALUE self)
     if (!tags)
        rb_raise (notmuch_rb_eMemoryError, "Out of memory");
 
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cTags, &notmuch_rb_tags_type, tags);
+    return notmuch_rb_tags_get (tags);
 }
index 8a2b4d3d48350e6b37ea9de73d86927ddb80e301..077def02f63021336de66d2c8c8e68edf1b4e6c4 100644 (file)
@@ -45,7 +45,7 @@ notmuch_rb_query_get_sort (VALUE self)
 
     Data_Get_Notmuch_Query (self, query);
 
-    return FIX2INT (notmuch_query_get_sort (query));
+    return INT2FIX (notmuch_query_get_sort (query));
 }
 
 /*
index 2af85e36598220f1cb93e806a8080ed2c3a3b862..b64874d1d4f441aabe8851338633871e8018886d 100644 (file)
@@ -1,56 +1,13 @@
-/* The Ruby interface to the notmuch mail library
- *
- * Copyright © 2010, 2011 Ali Polatel
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see https://www.gnu.org/licenses/ .
- *
- * Author: Ali Polatel <alip@exherbo.org>
- */
-
 #include "defs.h"
 
-/*
- * call-seq: TAGS.destroy! => nil
- *
- * Destroys the tags, freeing all resources allocated for it.
- */
-VALUE
-notmuch_rb_tags_destroy (VALUE self)
-{
-    notmuch_rb_object_destroy (self, &notmuch_rb_tags_type);
-
-    return Qnil;
-}
-
-/*
- * call-seq: TAGS.each {|item| block } => TAGS
- *
- * Calls +block+ once for each element in +self+, passing that element as a
- * parameter.
- */
 VALUE
-notmuch_rb_tags_each (VALUE self)
+notmuch_rb_tags_get (notmuch_tags_t *tags)
 {
-    const char *tag;
-    notmuch_tags_t *tags;
-
-    Data_Get_Notmuch_Tags (self, tags);
+    VALUE rb_array = rb_ary_new ();
 
     for (; notmuch_tags_valid (tags); notmuch_tags_move_to_next (tags)) {
-       tag = notmuch_tags_get (tags);
-       rb_yield (rb_str_new2 (tag));
+       const char *tag = notmuch_tags_get (tags);
+       rb_ary_push (rb_array, rb_str_new2 (tag));
     }
-
-    return self;
+    return rb_array;
 }
index 7cb2a3dcc6ebeacdd5c4c37b726a53056c436a5c..b20ed89376f802626d5cb4b0d847b020c18de196 100644 (file)
@@ -204,5 +204,5 @@ notmuch_rb_thread_get_tags (VALUE self)
     if (!tags)
        rb_raise (notmuch_rb_eMemoryError, "Out of memory");
 
-    return Data_Wrap_Notmuch_Object (notmuch_rb_cTags, &notmuch_rb_tags_type, tags);
+    return notmuch_rb_tags_get (tags);
 }
index 3cd1838d950bd30c6239053a09d2a8f401f8b537..8e004572dbf04865f538ba8d051b1745ae2fd495 100644 (file)
@@ -1,5 +1,6 @@
 #define _GNU_SOURCE
-#include <strings.h>
+#include <strings.h> /* strcasecmp() in POSIX */
+#include <string.h> /* strcasecmp() in *BSD */
 
 int
 main ()
index 0022b54bff5df41765aecdd640484a6dd53b5e5f..3748846edf8392ef80403bf352e0dd9318a80854 100644 (file)
@@ -530,7 +530,7 @@ _notmuch_show()
     ! $split &&
     case "${cur}" in
        -*)
-           local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt= --include-html ${_notmuch_shared_options}"
+           local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt= --include-html --limit= --offset= ${_notmuch_shared_options}"
            compopt -o nospace
            COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
            ;;
index e207d90b72020f0a94fa46c72702a34a6a1ddc35..0bdd7f772a7ae9be4f85a2b31bb1dc65d91a6864 100644 (file)
@@ -245,6 +245,8 @@ _notmuch_show() {
     '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
     '--body=[output body]:output body content:(true false)' \
     '--include-html[include text/html parts in the output]' \
+    '--limit=[limit the number of displayed results]:limit: ' \
+    '--offset=[skip displaying the first N results]:offset: ' \
     '*::search term:_notmuch_search_term'
 }
 
index be69b34d87e967dc7ca7b26b45f1564e343fd58e..7afd08c7dac7822b74cf3315b5ba6adacbdb1de7 100755 (executable)
--- a/configure
+++ b/configure
@@ -422,6 +422,18 @@ else
 fi
 unset test_cmdline
 
+printf "C compiler supports thread sanitizer... "
+test_cmdline="${CC} ${CFLAGS} ${CPPFLAGS} -fsanitize=thread minimal.c ${LDFLAGS} -o minimal"
+if ${test_cmdline} >/dev/null 2>&1 && ./minimal
+then
+    printf "Yes.\n"
+    have_tsan=1
+else
+    printf "Nope, skipping those tests.\n"
+    have_tsan=0
+fi
+unset test_cmdline
+
 printf "Reading libnotmuch version from source... "
 cat > _libversion.c <<EOF
 #include <stdio.h>
@@ -541,8 +553,8 @@ version of GPGME.
 Please try to rebuild your version of GMime against a more recent
 version of GPGME (at least GPGME 1.8.0).
 EOF
-       if command -v gpgme-config >/dev/null; then
-           printf 'Your current GPGME development version is: %s\n' "$(gpgme-config --version)"
+       if GPGME_VERS="$(pkg-config --modversion gpgme || gpgme-config --version)"; then
+           printf 'Your current GPGME development version is: %s\n' "$GPGME_VERS"
        else
            printf 'You do not have the GPGME development libraries installed.\n'
        fi
@@ -1590,8 +1602,9 @@ NOTMUCH_GMIME_VERIFY_WITH_SESSION_KEY=${gmime_verify_with_session_key}
 NOTMUCH_ZLIB_CFLAGS="${zlib_cflags}"
 NOTMUCH_ZLIB_LDFLAGS="${zlib_ldflags}"
 
-# Does the C compiler support the address sanitizer
+# Does the C compiler support the sanitizers
 NOTMUCH_HAVE_ASAN=${have_asan}
+NOTMUCH_HAVE_TSAN=${have_tsan}
 
 # do we have man pages?
 NOTMUCH_HAVE_MAN=$((have_sphinx))
index 26996c4ab2ccd1136b56e71e0c9d8c86c21a295a..c7520228782b2cacaf48e3b661c593013741e1ba 100644 (file)
@@ -39,8 +39,6 @@ To *run* notmuch-mutt you will need Perl with the following libraries:
   (Debian package: libmail-box-perl)
 - Mail::Header <https://metacpan.org/pod/Mail::Header>
   (Debian package: libmailtools-perl)
-- String::ShellQuote <https://metacpan.org/pod/String::ShellQuote>
-  (Debian package: libstring-shellquote-perl)
 - Term::ReadLine::Gnu <https://metacpan.org/pod/Term::ReadLine::Gnu>
   (Debian package: libterm-readline-gnu-perl)
 
index d1e2c084b4208f093f97a47e81169da28a65d9ad..b81252c809a8e3ed2fae56fd8e2bb14a6e56ed09 100755 (executable)
@@ -2,7 +2,7 @@
 #
 # notmuch-mutt - notmuch (of a) helper for Mutt
 #
-# Copyright: © 2011-2015 Stefano Zacchiroli <zack@upsilon.cc>
+# Copyright: Â© 2011-2015 Stefano Zacchiroli <zack@upsilon.cc>
 # License: GNU General Public License (GPL), version 3 or above
 #
 # See the bottom of this file for more documentation.
@@ -13,11 +13,11 @@ use warnings;
 
 use File::Path;
 use File::Basename;
+use File::Find;
 use Getopt::Long qw(:config no_getopt_compat);
 use Mail::Header;
 use Mail::Box::Maildir;
 use Pod::Usage;
-use String::ShellQuote;
 use Term::ReadLine;
 use Digest::SHA;
 
@@ -26,9 +26,53 @@ my $xdg_cache_dir = "$ENV{HOME}/.cache";
 $xdg_cache_dir = $ENV{XDG_CACHE_HOME} if $ENV{XDG_CACHE_HOME};
 my $cache_dir = "$xdg_cache_dir/notmuch/mutt";
 
+sub die_dir($$) {
+    my ($maildir, $error) = @_;
+    die "notmuch-mutt: search cache maildir $maildir $error\n".
+        "Please ensure that the notmuch-mutt search cache Maildir\n".
+        "contains no subfolders or real mail data, only symlinks to mail\n";
+}
+
+sub die_subdir($$$) {
+    my ($maildir, $subdir, $error) = @_;
+    die_dir($maildir, "subdir $subdir $error");
+}
 
-# create an empty maildir (if missing) or empty an existing maildir"
-sub empty_maildir($) {
+# check that the search cache maildir is that and not a real maildir
+# otherwise there could be data loss when the search cache is emptied
+sub check_search_cache_maildir($) {
+    my ($maildir) = (@_);
+
+    return unless -e $maildir;
+
+    -d $maildir or die_dir($maildir, 'is not a directory');
+
+    opendir(my $mdh, $maildir) or die_dir($maildir, "cannot be opened: $!");
+    my @contents = grep { !/^\.\.?$/ } readdir $mdh;
+    closedir $mdh;
+
+    my @required = ('cur', 'new', 'tmp');
+    foreach my $d (@required) {
+        -l "$maildir/$d" and die_dir($maildir, "contains symlink $d");
+        -e "$maildir/$d" or die_subdir($maildir, $d, 'is missing');
+        -d "$maildir/$d" or die_subdir($maildir, $d, 'is not a directory');
+        find(sub {
+            $_ eq '.' and return;
+            $_ eq '..' and return;
+            -l $_ or die_subdir($maildir, $d, "contains non-symlink $_");
+        }, "$maildir/$d");
+    }
+
+    my %required = map { $_ => 1 } @required;
+    foreach my $d (@contents) {
+        -l "$maildir/$d" and die_dir( $maildir, "contains symlink $d");
+        -d "$maildir/$d" or die_dir( $maildir, "contains non-directory $d");
+        exists($required{$d}) or die_dir( $maildir, "contains directory $d");
+    }
+}
+
+# create an empty search cache maildir (if missing) or empty existing one
+sub empty_search_cache_maildir($) {
     my ($maildir) = (@_);
     rmtree($maildir) if (-d $maildir);
     my $folder = new Mail::Box::Maildir(folder => $maildir,
@@ -46,7 +90,8 @@ sub search($$$) {
     push @args, "--duplicate=1" if $remove_dups;
     push @args, $query;
 
-    empty_maildir($maildir);
+    check_search_cache_maildir($maildir);
+    empty_search_cache_maildir($maildir);
     open my $pipe, '-|', @args or die "Running @args failed: $!\n";
     while (<$pipe>) {
        chomp;
@@ -121,21 +166,23 @@ sub thread_action($$@) {
 
     my $mid = get_message_id();
     if (! defined $mid) {
-       empty_maildir($results_dir);
        die "notmuch-mutt: cannot find Message-Id, abort.\n";
     }
-    my $search_cmd = 'notmuch search --output=threads ' . shell_quote("id:$mid");
-    my $tid = `$search_cmd`;   # get thread id
-    chomp($tid);
 
-    search($results_dir, $remove_dups, $tid);
+    $mid =~ s/ //g; # notmuch strips spaces before storing Message-Id
+    $mid =~ s/"/""""/g; # escape all double quote characters twice
+
+    search($results_dir, $remove_dups, qq{thread:"{id:""$mid""}"});
 }
 
 sub tag_action(@) {
     my $mid = get_message_id();
     defined $mid or die "notmuch-mutt: cannot find Message-Id, abort.\n";
 
-    system("notmuch", "tag", @_, "--", "id:$mid");
+    $mid =~ s/ //g; # notmuch strips spaces before storing Message-Id
+    $mid =~ s/"/""/g; # escape all double quote characters
+
+    system("notmuch", "tag", @_, "--", qq{id:"$mid"});
 }
 
 sub die_usage() {
index 846ccd2eab087523435d471aaa36557f907a055f..1177f085e9b067446e718b04455c19932b14072d 100644 (file)
@@ -1,3 +1,81 @@
+notmuch (0.38.3-1) unstable; urgency=medium
+
+  * New upstream bugfix release
+  * Bug fix: "Recommends transitional package gnupg-agent instead of
+    gpg-agent", thanks to Andreas Metzler (Closes: #1064114).
+
+ -- David Bremner <bremner@debian.org>  Sat, 09 Mar 2024 23:13:07 -0400
+
+notmuch (0.38.2-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Rename libraries for 64-bit time_t transition.  Closes: #1063205
+
+ -- Benjamin Drung <bdrung@debian.org>  Wed, 28 Feb 2024 23:56:48 +0000
+
+notmuch (0.38.2-1) unstable; urgency=medium
+
+  * New upstream bugfix release
+
+ -- David Bremner <bremner@debian.org>  Fri, 01 Dec 2023 07:51:09 -0400
+
+notmuch (0.38.1-1) unstable; urgency=medium
+
+  * New upstream bugfix release
+
+ -- David Bremner <bremner@debian.org>  Thu, 26 Oct 2023 19:58:42 -0300
+
+notmuch (0.38.1~rc1-1) experimental; urgency=medium
+
+  * New upstream release candidate
+
+ -- David Bremner <bremner@debian.org>  Thu, 12 Oct 2023 19:53:10 -0300
+
+notmuch (0.38.1~pre0-1) experimental; urgency=medium
+
+  * New upstream release candidate
+
+ -- David Bremner <bremner@debian.org>  Sun, 01 Oct 2023 08:14:17 -0300
+
+notmuch (0.38-2) unstable; urgency=medium
+
+  * Restrict autopkgtests to amd64 and aarch64. There are failures in
+    remaining architectures, but the same tests pass at build time, so any
+    bugs are probably related to either the autopkgtest environment, or
+    the (new) upstream test runner for installed notmuch.
+
+ -- David Bremner <bremner@debian.org>  Wed, 13 Sep 2023 19:55:00 -0300
+
+notmuch (0.38-1) unstable; urgency=medium
+
+  * New upstream release
+  * Bug fix: "FTBFS: 6 tests failed.", thanks to Aurelien Jarno (Closes:
+    #1051111).
+  * Run most of upstream test suite as autopkgtests
+
+ -- David Bremner <bremner@debian.org>  Tue, 12 Sep 2023 08:33:24 -0300
+
+notmuch (0.38~rc2-1) experimental; urgency=medium
+
+  * New upstream release candidate
+
+ -- David Bremner <bremner@debian.org>  Sun, 03 Sep 2023 09:10:24 -0300
+
+notmuch (0.38~rc1-1) experimental; urgency=medium
+
+  * New upstream release candidate
+  * Hopefully reduce/eliminate intermittent failures of T460 by
+    controlling Emacs native compilation.
+  * Disable T810-tsan on ppc64el
+
+ -- David Bremner <bremner@debian.org>  Sat, 26 Aug 2023 08:31:21 -0300
+
+notmuch (0.38~rc0-1) experimental; urgency=medium
+
+  * New upstream release candidate
+
+ -- David Bremner <bremner@debian.org>  Thu, 24 Aug 2023 10:56:06 -0300
+
 notmuch (0.37-1) unstable; urgency=medium
 
   * New upstream release.
index 2dcb8cc72f7caef524b07031323b98f3fc639e55..4fded909a03ca5d4910c2cc8f1fee4f03795ebbe 100644 (file)
@@ -9,7 +9,7 @@ Build-Conflicts:
  gdb [ia64 mips mips64el hppa],
  gdb-minimal,
  ruby1.8,
-Build-Depends:
+Build-Depends: dpkg-dev (>= 1.22.5),
  bash-completion (>=1.9.0~),
  debhelper-compat (= 13),
  dh-elpa (>= 1.3),
@@ -50,12 +50,12 @@ Rules-Requires-Root: no
 Package: notmuch
 Architecture: any
 Depends:
- libnotmuch5 (= ${binary:Version}),
+ libnotmuch5t64 (= ${binary:Version}),
  ${misc:Depends},
  ${shlibs:Depends},
 Recommends:
  elpa-notmuch | notmuch-vim | notmuch-mutt | alot,
- gnupg-agent,
+ gpg-agent,
  gpgsm,
 Suggests:
  mailscripts,
@@ -99,7 +99,10 @@ Description: thread-based email index, search and tagging
  .
  This package contains the HTML documentation
 
-Package: libnotmuch5
+Package: libnotmuch5t64
+Provides: ${t64:Provides}
+Replaces: libnotmuch5
+Breaks: libnotmuch5 (<< ${source:Version})
 Section: libs
 Architecture: any
 Depends:
@@ -120,7 +123,7 @@ Package: libnotmuch-dev
 Section: libdevel
 Architecture: any
 Depends:
- libnotmuch5 (= ${binary:Version}),
+ libnotmuch5t64 (= ${binary:Version}),
  ${misc:Depends},
 Description: thread-based email index, search and tagging (development)
  Notmuch is a system for indexing, searching, reading, and tagging
@@ -135,7 +138,7 @@ Package: python3-notmuch
 Architecture: all
 Section: python
 Depends:
- libnotmuch5 (>= ${source:Version}),
+ libnotmuch5t64 (>= ${source:Version}),
  ${misc:Depends},
  ${python3:Depends},
 Description: Python 3 legacy interface to the notmuch mail search and index library
@@ -153,7 +156,7 @@ Package: python3-notmuch2
 Architecture: any
 Section: python
 Depends:
- libnotmuch5 (>= ${source:Version}),
+ libnotmuch5t64 (>= ${source:Version}),
  ${misc:Depends},
  ${python3:Depends},
  ${shlibs:Depends},
@@ -227,7 +230,6 @@ Architecture: all
 Depends:
  libmail-box-perl,
  libmailtools-perl,
- libstring-shellquote-perl,
  libterm-readline-gnu-perl,
  notmuch (>= 0.4),
  ${misc:Depends},
diff --git a/debian/libnotmuch5.install b/debian/libnotmuch5.install
deleted file mode 100644 (file)
index a513b47..0000000
+++ /dev/null
@@ -1 +0,0 @@
-usr/lib/*/libnotmuch.so.*
diff --git a/debian/libnotmuch5.symbols b/debian/libnotmuch5.symbols
deleted file mode 100644 (file)
index e6d2b7c..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-libnotmuch.so.5 libnotmuch5 #MINVER#
-* Build-Depends-Package: libnotmuch-dev
- notmuch_built_with@Base 0.23~rc0
- notmuch_config_get@Base 0.32~rc0
- notmuch_config_get_bool@Base 0.32~rc0
- notmuch_config_get_pairs@Base 0.32~rc0
- notmuch_config_get_values@Base 0.32~rc0
- notmuch_config_get_values_string@Base 0.32~rc0
- notmuch_config_list_destroy@Base 0.23~rc0
- notmuch_config_list_key@Base 0.23~rc0
- notmuch_config_list_move_to_next@Base 0.23~rc0
- notmuch_config_list_valid@Base 0.23~rc0
- notmuch_config_list_value@Base 0.23~rc0
- notmuch_config_pairs_destroy@Base 0.32~rc0
- notmuch_config_pairs_key@Base 0.32~rc0
- notmuch_config_pairs_move_to_next@Base 0.32~rc0
- notmuch_config_pairs_valid@Base 0.32~rc0
- notmuch_config_pairs_value@Base 0.32~rc0
- notmuch_config_path@Base 0.32~rc0
- notmuch_config_set@Base 0.32~rc0
- notmuch_config_values_destroy@Base 0.32~rc0
- notmuch_config_values_get@Base 0.32~rc0
- notmuch_config_values_move_to_next@Base 0.32~rc0
- notmuch_config_values_start@Base 0.32~rc0
- notmuch_config_values_valid@Base 0.32~rc0
- notmuch_database_add_message@Base 0.3
- notmuch_database_begin_atomic@Base 0.9~rc1
- notmuch_database_close@Base 0.13~rc1
- notmuch_database_compact@Base 0.17~rc1
- notmuch_database_compact_db@Base 0.32~rc0
- notmuch_database_create@Base 0.3
- notmuch_database_create_verbose@Base 0.20~rc1
- notmuch_database_create_with_config@Base 0.32~rc0
- notmuch_database_destroy@Base 0.13~rc1
- notmuch_database_end_atomic@Base 0.9~rc1
- notmuch_database_find_message@Base 0.9~rc2
- notmuch_database_find_message_by_filename@Base 0.9~rc2
- notmuch_database_get_all_tags@Base 0.3
- notmuch_database_get_config@Base 0.23~rc0
- notmuch_database_get_config_list@Base 0.23~rc0
- notmuch_database_get_default_indexopts@Base 0.26~rc0
- notmuch_database_get_directory@Base 0.3
- notmuch_database_get_path@Base 0.3
- notmuch_database_get_revision@Base 0.21~rc1
- notmuch_database_get_version@Base 0.3
- notmuch_database_index_file@Base 0.26~rc0
- notmuch_database_load_config@Base 0.32~rc0
- notmuch_database_needs_upgrade@Base 0.3
- notmuch_database_open@Base 0.3
- notmuch_database_open_verbose@Base 0.20~rc1
- notmuch_database_open_with_config@Base 0.32~rc0
- notmuch_database_remove_message@Base 0.3
- notmuch_database_reopen@Base 0.32~rc0
- notmuch_database_set_config@Base 0.23~rc0
- notmuch_database_status_string@Base 0.20~rc1
- notmuch_database_upgrade@Base 0.3
- notmuch_directory_delete@Base 0.21~rc1
- notmuch_directory_destroy@Base 0.3
- notmuch_directory_get_child_directories@Base 0.3
- notmuch_directory_get_child_files@Base 0.3
- notmuch_directory_get_mtime@Base 0.3
- notmuch_directory_set_mtime@Base 0.3
- notmuch_filenames_destroy@Base 0.3
- notmuch_filenames_get@Base 0.3
- notmuch_filenames_move_to_next@Base 0.3
- notmuch_filenames_valid@Base 0.3
- notmuch_indexopts_destroy@Base 0.26~rc0
- notmuch_indexopts_get_decrypt_policy@Base 0.26~rc0
- notmuch_indexopts_set_decrypt_policy@Base 0.26~rc0
- notmuch_message_add_property@Base 0.23~rc0
- notmuch_message_add_tag@Base 0.3
- notmuch_message_count_files@Base 0.26~rc0
- notmuch_message_count_properties@Base 0.27~rc0
- notmuch_message_destroy@Base 0.3
- notmuch_message_freeze@Base 0.3
- notmuch_message_get_database@Base 0.27~rc0
- notmuch_message_get_date@Base 0.3
- notmuch_message_get_filename@Base 0.3
- notmuch_message_get_filenames@Base 0.5
- notmuch_message_get_flag@Base 0.3
- notmuch_message_get_flag_st@Base 0.31~rc0
- notmuch_message_get_header@Base 0.3
- notmuch_message_get_message_id@Base 0.3
- notmuch_message_get_properties@Base 0.23~rc0
- notmuch_message_get_property@Base 0.23~rc0
- notmuch_message_get_replies@Base 0.3
- notmuch_message_get_tags@Base 0.3
- notmuch_message_get_thread_id@Base 0.3
- notmuch_message_has_maildir_flag@Base 0.26~rc0
- notmuch_message_has_maildir_flag_st@Base 0.31~rc0
- notmuch_message_maildir_flags_to_tags@Base 0.5
- notmuch_message_properties_destroy@Base 0.23~rc0
- notmuch_message_properties_key@Base 0.23~rc0
- notmuch_message_properties_move_to_next@Base 0.23~rc0
- notmuch_message_properties_valid@Base 0.23~rc0
- notmuch_message_properties_value@Base 0.23~rc0
- notmuch_message_reindex@Base 0.26~rc0
- notmuch_message_remove_all_properties@Base 0.23~rc0
- notmuch_message_remove_all_properties_with_prefix@Base 0.26~rc0
- notmuch_message_remove_all_tags@Base 0.3
- notmuch_message_remove_property@Base 0.23~rc0
- notmuch_message_remove_tag@Base 0.3
- notmuch_message_set_flag@Base 0.3
- notmuch_message_tags_to_maildir_flags@Base 0.5
- notmuch_message_thaw@Base 0.3
- notmuch_messages_collect_tags@Base 0.3
- notmuch_messages_destroy@Base 0.3
- notmuch_messages_get@Base 0.3
- notmuch_messages_move_to_next@Base 0.3
- notmuch_messages_valid@Base 0.3
- notmuch_query_add_tag_exclude@Base 0.12~rc1
- notmuch_query_count_messages@Base 0.3
- notmuch_query_count_messages_st@Base 0.21~rc1
- notmuch_query_count_threads@Base 0.10~rc1
- notmuch_query_count_threads_st@Base 0.21~rc1
- notmuch_query_create@Base 0.3
- notmuch_query_create_with_syntax@Base 0.34~rc0
- notmuch_query_destroy@Base 0.3
- notmuch_query_get_database@Base 0.21~rc1
- notmuch_query_get_query_string@Base 0.4
- notmuch_query_get_sort@Base 0.4
- notmuch_query_search_messages@Base 0.3
- notmuch_query_search_messages_st@Base 0.20~rc1
- notmuch_query_search_threads@Base 0.3
- notmuch_query_search_threads_st@Base 0.20~rc1
- notmuch_query_set_omit_excluded@Base 0.13~rc1
- notmuch_query_set_sort@Base 0.3
- notmuch_status_to_string@Base 0.3
- notmuch_tags_destroy@Base 0.3
- notmuch_tags_get@Base 0.3
- notmuch_tags_move_to_next@Base 0.3
- notmuch_tags_valid@Base 0.3
- notmuch_thread_destroy@Base 0.3
- notmuch_thread_get_authors@Base 0.3
- notmuch_thread_get_matched_messages@Base 0.3
- notmuch_thread_get_messages@Base 0.16
- notmuch_thread_get_newest_date@Base 0.3
- notmuch_thread_get_oldest_date@Base 0.3
- notmuch_thread_get_subject@Base 0.3
- notmuch_thread_get_tags@Base 0.3
- notmuch_thread_get_thread_id@Base 0.3
- notmuch_thread_get_toplevel_messages@Base 0.3
- notmuch_thread_get_total_files@Base 0.26~rc0
- notmuch_thread_get_total_messages@Base 0.3
- notmuch_threads_destroy@Base 0.3
- notmuch_threads_get@Base 0.3
- notmuch_threads_move_to_next@Base 0.3
- notmuch_threads_valid@Base 0.3
- (c++)"typeinfo for Xapian::LogicError@Base" 0.6.1
- (c++)"typeinfo for Xapian::RuntimeError@Base" 0.6.1
- (c++)"typeinfo for Xapian::DocNotFoundError@Base" 0.6.1
- (c++)"typeinfo for Xapian::InvalidArgumentError@Base" 0.6.1
- (c++)"typeinfo for Xapian::Error@Base" 0.6.1
- (c++)"typeinfo for Xapian::DatabaseError@Base" 0.24~rc0
- (c++)"typeinfo for Xapian::DatabaseModifiedError@Base" 0.24~rc0
- (c++)"typeinfo for Xapian::DatabaseOpeningError@Base" 0.32~rc0
- (c++|optional=present with Xapian 1.4)"typeinfo for Xapian::QueryParserError@Base" 0.23~rc0
- (c++)"typeinfo name for Xapian::LogicError@Base" 0.6.1
- (c++)"typeinfo name for Xapian::RuntimeError@Base" 0.6.1
- (c++)"typeinfo name for Xapian::DocNotFoundError@Base" 0.6.1
- (c++)"typeinfo name for Xapian::InvalidArgumentError@Base" 0.6.1
- (c++)"typeinfo name for Xapian::Error@Base" 0.6.1
- (c++)"typeinfo name for Xapian::DatabaseError@Base" 0.24~rc0
- (c++)"typeinfo name for Xapian::DatabaseModifiedError@Base" 0.24~rc0
- (c++)"typeinfo name for Xapian::DatabaseOpeningError@Base" 0.32~rc0
- (c++|optional=present with Xapian 1.4)"typeinfo name for Xapian::QueryParserError@Base" 0.23~rc0
diff --git a/debian/libnotmuch5t64.install b/debian/libnotmuch5t64.install
new file mode 100644 (file)
index 0000000..a513b47
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/*/libnotmuch.so.*
diff --git a/debian/libnotmuch5t64.lintian-overrides b/debian/libnotmuch5t64.lintian-overrides
new file mode 100644 (file)
index 0000000..affb63b
--- /dev/null
@@ -0,0 +1 @@
+libnotmuch5t64: package-name-doesnt-match-sonames libnotmuch5
diff --git a/debian/libnotmuch5t64.symbols b/debian/libnotmuch5t64.symbols
new file mode 100644 (file)
index 0000000..5715dec
--- /dev/null
@@ -0,0 +1,166 @@
+libnotmuch.so.5 libnotmuch5t64 #MINVER#
+* Build-Depends-Package: libnotmuch-dev
+ notmuch_built_with@Base 0.23~rc0
+ notmuch_config_get@Base 0.32~rc0
+ notmuch_config_get_bool@Base 0.32~rc0
+ notmuch_config_get_pairs@Base 0.32~rc0
+ notmuch_config_get_values@Base 0.32~rc0
+ notmuch_config_get_values_string@Base 0.32~rc0
+ notmuch_config_list_destroy@Base 0.23~rc0
+ notmuch_config_list_key@Base 0.23~rc0
+ notmuch_config_list_move_to_next@Base 0.23~rc0
+ notmuch_config_list_valid@Base 0.23~rc0
+ notmuch_config_list_value@Base 0.23~rc0
+ notmuch_config_pairs_destroy@Base 0.32~rc0
+ notmuch_config_pairs_key@Base 0.32~rc0
+ notmuch_config_pairs_move_to_next@Base 0.32~rc0
+ notmuch_config_pairs_valid@Base 0.32~rc0
+ notmuch_config_pairs_value@Base 0.32~rc0
+ notmuch_config_path@Base 0.32~rc0
+ notmuch_config_set@Base 0.32~rc0
+ notmuch_config_values_destroy@Base 0.32~rc0
+ notmuch_config_values_get@Base 0.32~rc0
+ notmuch_config_values_move_to_next@Base 0.32~rc0
+ notmuch_config_values_start@Base 0.32~rc0
+ notmuch_config_values_valid@Base 0.32~rc0
+ notmuch_database_add_message@Base 0.3
+ notmuch_database_begin_atomic@Base 0.9~rc1
+ notmuch_database_close@Base 0.13~rc1
+ notmuch_database_compact@Base 0.17~rc1
+ notmuch_database_compact_db@Base 0.32~rc0
+ notmuch_database_create@Base 0.3
+ notmuch_database_create_verbose@Base 0.20~rc1
+ notmuch_database_create_with_config@Base 0.32~rc0
+ notmuch_database_destroy@Base 0.13~rc1
+ notmuch_database_end_atomic@Base 0.9~rc1
+ notmuch_database_find_message@Base 0.9~rc2
+ notmuch_database_find_message_by_filename@Base 0.9~rc2
+ notmuch_database_get_all_tags@Base 0.3
+ notmuch_database_get_config@Base 0.23~rc0
+ notmuch_database_get_config_list@Base 0.23~rc0
+ notmuch_database_get_default_indexopts@Base 0.26~rc0
+ notmuch_database_get_directory@Base 0.3
+ notmuch_database_get_path@Base 0.3
+ notmuch_database_get_revision@Base 0.21~rc1
+ notmuch_database_get_version@Base 0.3
+ notmuch_database_index_file@Base 0.26~rc0
+ notmuch_database_load_config@Base 0.32~rc0
+ notmuch_database_needs_upgrade@Base 0.3
+ notmuch_database_open@Base 0.3
+ notmuch_database_open_verbose@Base 0.20~rc1
+ notmuch_database_open_with_config@Base 0.32~rc0
+ notmuch_database_remove_message@Base 0.3
+ notmuch_database_reopen@Base 0.32~rc0
+ notmuch_database_set_config@Base 0.23~rc0
+ notmuch_database_status_string@Base 0.20~rc1
+ notmuch_database_upgrade@Base 0.3
+ notmuch_directory_delete@Base 0.21~rc1
+ notmuch_directory_destroy@Base 0.3
+ notmuch_directory_get_child_directories@Base 0.3
+ notmuch_directory_get_child_files@Base 0.3
+ notmuch_directory_get_mtime@Base 0.3
+ notmuch_directory_set_mtime@Base 0.3
+ notmuch_filenames_destroy@Base 0.3
+ notmuch_filenames_get@Base 0.3
+ notmuch_filenames_move_to_next@Base 0.3
+ notmuch_filenames_valid@Base 0.3
+ notmuch_indexopts_destroy@Base 0.26~rc0
+ notmuch_indexopts_get_decrypt_policy@Base 0.26~rc0
+ notmuch_indexopts_set_decrypt_policy@Base 0.26~rc0
+ notmuch_message_add_property@Base 0.23~rc0
+ notmuch_message_add_tag@Base 0.3
+ notmuch_message_count_files@Base 0.26~rc0
+ notmuch_message_count_properties@Base 0.27~rc0
+ notmuch_message_destroy@Base 0.3
+ notmuch_message_freeze@Base 0.3
+ notmuch_message_get_database@Base 0.27~rc0
+ notmuch_message_get_date@Base 0.3
+ notmuch_message_get_filename@Base 0.3
+ notmuch_message_get_filenames@Base 0.5
+ notmuch_message_get_flag@Base 0.3
+ notmuch_message_get_flag_st@Base 0.31~rc0
+ notmuch_message_get_header@Base 0.3
+ notmuch_message_get_message_id@Base 0.3
+ notmuch_message_get_properties@Base 0.23~rc0
+ notmuch_message_get_property@Base 0.23~rc0
+ notmuch_message_get_replies@Base 0.3
+ notmuch_message_get_tags@Base 0.3
+ notmuch_message_get_thread_id@Base 0.3
+ notmuch_message_has_maildir_flag@Base 0.26~rc0
+ notmuch_message_has_maildir_flag_st@Base 0.31~rc0
+ notmuch_message_maildir_flags_to_tags@Base 0.5
+ notmuch_message_properties_destroy@Base 0.23~rc0
+ notmuch_message_properties_key@Base 0.23~rc0
+ notmuch_message_properties_move_to_next@Base 0.23~rc0
+ notmuch_message_properties_valid@Base 0.23~rc0
+ notmuch_message_properties_value@Base 0.23~rc0
+ notmuch_message_reindex@Base 0.26~rc0
+ notmuch_message_remove_all_properties@Base 0.23~rc0
+ notmuch_message_remove_all_properties_with_prefix@Base 0.26~rc0
+ notmuch_message_remove_all_tags@Base 0.3
+ notmuch_message_remove_property@Base 0.23~rc0
+ notmuch_message_remove_tag@Base 0.3
+ notmuch_message_set_flag@Base 0.3
+ notmuch_message_tags_to_maildir_flags@Base 0.5
+ notmuch_message_thaw@Base 0.3
+ notmuch_messages_collect_tags@Base 0.3
+ notmuch_messages_destroy@Base 0.3
+ notmuch_messages_get@Base 0.3
+ notmuch_messages_move_to_next@Base 0.3
+ notmuch_messages_valid@Base 0.3
+ notmuch_query_add_tag_exclude@Base 0.12~rc1
+ notmuch_query_count_messages@Base 0.3
+ notmuch_query_count_messages_st@Base 0.21~rc1
+ notmuch_query_count_threads@Base 0.10~rc1
+ notmuch_query_count_threads_st@Base 0.21~rc1
+ notmuch_query_create@Base 0.3
+ notmuch_query_create_with_syntax@Base 0.34~rc0
+ notmuch_query_destroy@Base 0.3
+ notmuch_query_get_database@Base 0.21~rc1
+ notmuch_query_get_query_string@Base 0.4
+ notmuch_query_get_sort@Base 0.4
+ notmuch_query_search_messages@Base 0.3
+ notmuch_query_search_messages_st@Base 0.20~rc1
+ notmuch_query_search_threads@Base 0.3
+ notmuch_query_search_threads_st@Base 0.20~rc1
+ notmuch_query_set_omit_excluded@Base 0.13~rc1
+ notmuch_query_set_sort@Base 0.3
+ notmuch_status_to_string@Base 0.3
+ notmuch_tags_destroy@Base 0.3
+ notmuch_tags_get@Base 0.3
+ notmuch_tags_move_to_next@Base 0.3
+ notmuch_tags_valid@Base 0.3
+ notmuch_thread_destroy@Base 0.3
+ notmuch_thread_get_authors@Base 0.3
+ notmuch_thread_get_matched_messages@Base 0.3
+ notmuch_thread_get_messages@Base 0.16
+ notmuch_thread_get_newest_date@Base 0.3
+ notmuch_thread_get_oldest_date@Base 0.3
+ notmuch_thread_get_subject@Base 0.3
+ notmuch_thread_get_tags@Base 0.3
+ notmuch_thread_get_thread_id@Base 0.3
+ notmuch_thread_get_toplevel_messages@Base 0.3
+ notmuch_thread_get_total_files@Base 0.26~rc0
+ notmuch_thread_get_total_messages@Base 0.3
+ notmuch_threads_destroy@Base 0.3
+ notmuch_threads_get@Base 0.3
+ notmuch_threads_move_to_next@Base 0.3
+ notmuch_threads_valid@Base 0.3
+ (c++)"typeinfo for Xapian::LogicError@Base" 0.6.1
+ (c++)"typeinfo for Xapian::RuntimeError@Base" 0.6.1
+ (c++)"typeinfo for Xapian::DocNotFoundError@Base" 0.6.1
+ (c++)"typeinfo for Xapian::InvalidArgumentError@Base" 0.6.1
+ (c++)"typeinfo for Xapian::Error@Base" 0.6.1
+ (c++)"typeinfo for Xapian::DatabaseError@Base" 0.24~rc0
+ (c++)"typeinfo for Xapian::DatabaseModifiedError@Base" 0.24~rc0
+ (c++)"typeinfo for Xapian::DatabaseOpeningError@Base" 0.32~rc0
+ (c++|optional=present with Xapian 1.4)"typeinfo for Xapian::QueryParserError@Base" 0.23~rc0
+ (c++)"typeinfo name for Xapian::LogicError@Base" 0.6.1
+ (c++)"typeinfo name for Xapian::RuntimeError@Base" 0.6.1
+ (c++)"typeinfo name for Xapian::DocNotFoundError@Base" 0.6.1
+ (c++)"typeinfo name for Xapian::InvalidArgumentError@Base" 0.6.1
+ (c++)"typeinfo name for Xapian::Error@Base" 0.6.1
+ (c++)"typeinfo name for Xapian::DatabaseError@Base" 0.24~rc0
+ (c++)"typeinfo name for Xapian::DatabaseModifiedError@Base" 0.24~rc0
+ (c++)"typeinfo name for Xapian::DatabaseOpeningError@Base" 0.32~rc0
+ (c++|optional=present with Xapian 1.4)"typeinfo name for Xapian::QueryParserError@Base" 0.23~rc0
index f9196b82612001ee89a4a1d5753c29901c9ba9d2..a77ffa152daefd3ca8a611121cc3509a9e5c341d 100755 (executable)
@@ -1,4 +1,9 @@
 #!/usr/bin/make -f
+include /usr/share/dpkg/architecture.mk
+
+ifeq ($(DEB_HOST_ARCH),ppc64el)
+       export NOTMUCH_SKIP_TESTS = T810-tsan
+endif
 
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
@@ -7,7 +12,7 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
 override_dh_auto_configure:
        BASHCMD=/bin/bash ./configure --prefix=/usr \
-               --libdir=/usr/lib/$$(dpkg-architecture -q DEB_TARGET_MULTIARCH) \
+               --libdir=/usr/lib/${DEB_TARGET_MULTIARCH} \
                --includedir=/usr/include \
                --mandir=/usr/share/man \
                --infodir=/usr/share/info \
@@ -24,7 +29,6 @@ override_dh_auto_build:
 override_dh_auto_clean:
        dh_auto_clean
        PYBUILD_NAME=notmuch dh_auto_clean --buildsystem=pybuild --sourcedirectory bindings/python
-       PYBUILD_NAME=notmuch2 dh_auto_clean --buildsystem=pybuild --sourcedirectory bindings/python-cffi
        dh_auto_clean --sourcedirectory bindings/ruby
        $(MAKE) -C contrib/notmuch-mutt clean
 
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644 (file)
index 0000000..80be1de
--- /dev/null
@@ -0,0 +1,18 @@
+Test-command: env NOTMUCH_TEST_INSTALLED=1 TERM=dumb
+    NOTMUCH_HAVE_MAN=1 NOTMUCH_HAVE_SFSEXP=1 NOTMUCH_HAVE_XAPIAN_DB_RETRY_LOCK=1
+    NOTMUCH_HAVE_PYTHON3_CFFI=1 NOTMUCH_HAVE_PYTHON3_PYTEST=1
+    NOTMUCH_HAVE_ASAN=1 NOTMUCH_HAVE_TSAN=1
+    ./test/notmuch-test
+Restrictions: allow-stderr
+Architecture: amd64, arm64
+Depends: @,
+ build-essential,
+ dtach,
+ emacs-nox,
+ gdb,
+ git,
+ gnupg,
+ gpgsm,
+ libtalloc-dev,
+ man,
+ xapian-tools
index ad7f72ef0bb3ce039a9bd3fd22988c93b80565c6..218677c2960dc6477c350de7066dd8844e3c815c 100644 (file)
@@ -9,7 +9,7 @@
 | f            |                                        | notmuch-show-forward-message                          | notmuch-show-forward-message            |
 | g            |                                        |                                                       |                                         |
 | h            |                                        | notmuch-show-toggle-visibility-headers                |                                         |
-| i            |                                        |                                                       |                                         |
+| i            | notmuch-search-toggle-hide-excluded    |                                                       | notmuch-tree-toggle-hide-excluded       |
 | j            | notmuch-jump-search                    | notmuch-jump-search                                   | notmuch-jump-search                     |
 | k            | notmuch-tag-jump                       | notmuch-tag-jump                                      | notmuch-tag-jump                        |
 | l            | notmuch-search-filter                  | notmuch-show-filter-thread                            | notmuch-tree-filter                     |
index 7b555c62b6eeadd3deadaa2cec5584fa4edf535f..e0e87b49b2ca2c0de3fc29a36e58fd498841c960 100755 (executable)
@@ -125,7 +125,7 @@ def mailto_addrs(msg,header_name):
     return ''
 
   frm = email.utils.getaddresses([hdr])
-  return ','.join(['<a href="mailto:%s">%s</a> ' % ((l, p) if p else (l, l)) for (p, l) in frm])
+  return ', '.join(['<a href="mailto:%s">%s</a>' % ((l, p) if p else (l, l)) for (p, l) in frm])
 env.globals['mailto_addrs'] = mailto_addrs
 
 def link_msg(msg):
@@ -207,7 +207,7 @@ env.globals['thread_nav'] = thread_nav
 
 def format_message(nm_msg, mid):
   fn = list(nm_msg.filenames())[0]
-  msg = MaildirMessage(open(fn))
+  msg = MaildirMessage(open(fn, 'rb'))
   return format_message_walk(msg, mid)
 
 def decodeAnyway(txt, charset='ascii'):
index 98d36accb95b6c6325e39e8eb8ef7fd6e8513ec4..690f5464ef053a1baf24edec1235b56e0bbe344c 100644 (file)
@@ -3,10 +3,10 @@
 {% set headers = ['Subject', 'Date'] %}
 {% set addr_headers = ['To', 'Cc', 'From'] %}
 {% for header in headers: %}
-<p><b>{{header}}:</b>{{m.header(header)|e}}</p>
+<p><b>{{header}}:</b> {{m.header(header)|e}}</p>
 {% endfor %}
 {% for header in addr_headers: %}
-<p><b>{{header}}:</b>{{mailto_addrs(m,header)|safe}}</p>
+<p><b>{{header}}:</b> {{mailto_addrs(m,header)|safe}}</p>
 {% endfor %}
 <hr>
 {% for part in format_message(m,mid): %}{{ part|safe }}{% endfor %}
index 66bcdbed8d0146967b10260bfb6a599cc3bb63b0..4e05cdacdf0419718f94065a7c58169ca9138afc 100644 (file)
@@ -76,6 +76,7 @@ message = {
     # (format_message_sprinter)
     id:             messageid,
     match:          bool,
+    excluded:       bool,
     filename:      [string*],
     timestamp:      unix_time, # date header as unix time
     date_relative:  string,   # user-friendly timestamp
index 51c729cf7fe5d61ef5719e25c797ecff62d40af5..aafa77a0fe229e7dc2f8435e6e7791c5dc8619ae 100644 (file)
@@ -159,3 +159,5 @@ $(dir)/config.dox: version.stamp
 CLEAN := $(CLEAN) $(DOCBUILDDIR) $(DOCBUILDDIR)/.roff.stamp $(DOCBUILDDIR)/.texi.stamp
 CLEAN := $(CLEAN) $(DOCBUILDDIR)/.html.stamp $(DOCBUILDDIR)/.info.stamp
 CLEAN := $(CLEAN) $(MAN_GZIP_FILES) $(MAN_ROFF_FILES) $(dir)/conf.pyc $(dir)/config.dox
+
+CLEAN := $(CLEAN) $(dir)/__pycache__
index e23cb7d7fb444964cb341269771629713f8c2d2e..ee1b336af2ad61d36336c5891b821cf201915560 100644 (file)
@@ -16,7 +16,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'notmuch'
-copyright = u'2009-2022, Carl Worth and many others'
+copyright = u'2009-2024, Carl Worth and many others'
 
 location = os.path.dirname(__file__)
 
index 388315f67ec609c396ee0168c7e9741b9990bbec..bd34afa40ee6975a7f1aa316bd4e22e3f1605070 100644 (file)
@@ -122,6 +122,16 @@ paths are presumed relative to `$HOME` for items in section
 
     Default tag prefix (filter) for :any:`notmuch-git`.
 
+.. nmconfig:: index.as_text
+
+   List of regular expressions (without delimiters) for MIME types to
+   be indexed as text. Currently this applies only to attachments.  By
+   default the regex matches anywhere in the content type; if the
+   user wants an anchored match, they should include anchors in their
+   regexes.
+
+   History: This configuration value was introduced in notmuch 0.38.
+
 .. nmconfig:: index.decrypt
 
     Policy for decrypting encrypted messages during indexing.  Must be
index ac1908b6103f17be1aa2e995f4dffbf163ab0f87..33a46f84058750ef19b910772089bbe07f7956ed 100644 (file)
@@ -325,11 +325,11 @@ If it is unset, 'default' is assumed.
 
 .. envvar:: NOTMUCH_GIT_DIR
 
-   Default location of git repository. Overriden by :option:`--git-dir`.
+   Default location of git repository. Overridden by :option:`--git-dir`.
 
 .. envvar:: NOTMUCH_GIT_PREFIX
 
-   Default tag prefix (filter). Overriden by :option:`--tag-prefix`.
+   Default tag prefix (filter). Overridden by :option:`--tag-prefix`.
 
 SEE ALSO
 ========
index ad305efdb6a07a2ff2635c618da21fcce2c9ad9d..b87737ea3bd0ae4b2ba0ec10a0a7014cc971ff2b 100644 (file)
@@ -43,7 +43,7 @@ Supported options for **search** include
 
 .. option:: --output=(summary|threads|messages|files|tags)
 
-   summary
+   summary (default)
      Output a summary of each thread with any message matching the
      search terms. The summary includes the thread ID, date, the
      number of messages in the thread (both the number matched and
index 2c0a0de6ad1679ccc2d2596acd3c8936a968e49c..c13d94de024400fa2ca9642424ab6ec03284ad56 100644 (file)
@@ -130,6 +130,15 @@ Supported options for **show** include
    By default, results will be displayed in reverse chronological
    order, (that is, the newest results will be displayed first).
 
+.. option:: --offset=[-]N
+
+   Skip displaying the first N results. With the leading '-', start
+   at the Nth result from the end.
+
+.. option:: --limit=N
+
+   Limit the number of displayed results to N.
+
 .. option:: --verify
 
    Compute and report the validity of any MIME cryptographic
index 422154c7a3303cc5987b4fe3af3691d3c77371a1..858ff6850fd3dc6fb6c978e0fdac30f1adafbe30 100644 (file)
@@ -274,7 +274,7 @@ EXAMPLES
 
     Matches any word starting with "prelim", inside a message subject.
 
-``(subject (starts-wih quick) "brown fox")``
+``(subject (starts-with quick) "brown fox")``
 
     Match messages whose subject contains "quick brown fox", but also
     "brown fox quicksand".
@@ -336,7 +336,7 @@ user defined fields is permitted within a macro.
 NOTES
 =====
 
-.. [#macro-details] Technically macros impliment lazy evaluation and
+.. [#macro-details] Technically macros implement lazy evaluation and
                     lexical scope. There is one top level scope
                     containing all macro definitions, but all
                     parameter definitions are local to a given macro.
@@ -347,10 +347,10 @@ NOTES
 
 .. [#aka-bool] a.k.a. boolean prefixes
 
-.. [#not-phrase] Due to the implemention of phrase fields in Xapian,
+.. [#not-phrase] Due to the implementation of phrase fields in Xapian,
                  regex queries could only match individual words.
 
-.. [#not-body] Due the the way ``body`` is implemented in notmuch,
+.. [#not-body] Due to the way ``body`` is implemented in notmuch,
                this modifier is not supported in the ``body`` field.
 
 .. [#not-path] Due to the way recursive ``path`` queries are implemented
index 846f5e671cf292b3bf3ddeedde94acd7b99da94f..91af6d146f0d9682d125bd38a879d9941245d634 100644 (file)
@@ -14,7 +14,7 @@ manual to refer to the Emacs interface to Notmuch. When this distinction
 is important, we’ll refer to the Emacs interface as
 *notmuch-emacs*.
 
-Notmuch-emacs is highly customizable via the the Emacs customization
+Notmuch-emacs is highly customizable via the Emacs customization
 framework (or just by setting the appropriate variables). We try to
 point out relevant variables in this manual, but in order to avoid
 duplication of information, you can usually find the most detailed
@@ -232,6 +232,10 @@ menu of results that the user can explore further by pressing
 
     Refresh the buffer
 
+.. el:define-key:: i
+
+    Toggle whether to show messages with excluded tags in search results.
+
 .. el:define-key:: ?
 
     Display full set of key bindings
@@ -493,7 +497,7 @@ in :ref:`notmuch-search`.
 Dealing with duplicates
 -----------------------
 
-If there are are multiple files with the same :mailheader:`Message-ID`
+If there are multiple files with the same :mailheader:`Message-ID`
 (see :any:`duplicate-files`), then :any:`notmuch-show` displays the
 number of duplicates and identifies the current duplicate. In the
 following example duplicate 3 of 5 is displayed.
@@ -559,6 +563,9 @@ tags.
 
    Filter the current search results based on an additional tag
 
+.. el:define-key:: i
+
+    Toggle whether to show messages with excluded tags in search results.
 
 .. el:define-key:: g
    =
@@ -606,6 +613,45 @@ can be controlled by the variable ``notmuch-search-oldest-first``.
    See also :el:defcustom:`notmuch-search-result-format` and
    :el:defcustom:`notmuch-unthreaded-result-format`.
 
+.. _notmuch-tree-outline:
+
+notmuch-tree-outline
+--------------------
+
+When this mode is set, each thread and subthread in the results
+list is treated as a foldable section, with its first message as
+its header.
+
+The mode just makes available in the tree buffer all the
+keybindings in info:emacs#Outline_Mode, and binds the following
+additional keys:
+
+.. el:define-key:: <tab>
+
+   Cycle visibility state of the current message's tree.
+
+.. el:define-key:: <M-tab>
+
+   Cycle visibility state of all trees in the buffer.
+
+The behaviour of this minor mode is affected by the following
+customizable variables:
+
+.. el:defcustom:: notmuch-tree-outline-enabled
+
+   |docstring::notmuch-tree-outline-enabled|
+
+.. el:defcustom:: notmuch-tree-outline-visibility
+
+   |docstring::notmuch-tree-outline-visibility|
+
+.. el:defcustom:: notmuch-tree-outline-auto-close
+
+   |docstring::notmuch-tree-outline-auto-close|
+
+.. el:defcustom:: notmuch-tree-outline-open-on-next
+
+   |docstring::notmuch-tree-outline-open-on-next|
 
 .. _notmuch-unthreaded:
 
@@ -678,7 +724,7 @@ operations specified in ``notmuch-tagging-keys``; i.e. each
 notmuch-tag-undo
 ----------------
 
-Each notmuch buffer supporting tagging operations (i.e buffers in
+Each notmuch buffer supporting tagging operations (i.e. buffers in
 :any:`notmuch-show`, :any:`notmuch-search`, :any:`notmuch-tree`, and
 :any:`notmuch-unthreaded` mode) keeps a local stack of tagging
 operations. These can be undone via :any:`notmuch-tag-undo`. By default
index 1a4cdda22b95ffee9434b589f0912700b8c22b02..f756254cb8513ec062ca692299fbb39e4f683fe1 100644 (file)
@@ -153,8 +153,7 @@ matching `notmuch-address-completion-headers-regexp'."
 ;;; Setup
 
 (defun notmuch-address-selection-function (prompt collection initial-input)
-  "Call (`completing-read'
-      PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)"
+  "Default address selection function: delegate to completing read."
   (completing-read
    prompt collection nil nil initial-input 'notmuch-address-history))
 
index a521497784ec27e91cf9c7ee905641b3d4459012..254e6407cecefbbd8656321d9283cf63d360afca 100755 (executable)
@@ -41,6 +41,9 @@ CREATE_FRAME=
 ELISP=
 MAILTO=
 HELLO=
+TO_SEP=
+CC_SEP=
+BCC_SEP=
 
 # Short options compatible with mutt(1).
 while getopts :s:c:b:i:h opt; do
@@ -86,13 +89,16 @@ while getopts :s:c:b:i:h opt; do
            ELISP="${ELISP} (message-goto-subject) (insert \"${OPTARG}\")"
            ;;
        --to)
-           ELISP="${ELISP} (message-goto-to) (insert \"${OPTARG}, \")"
+           ELISP="${ELISP} (message-goto-to) (insert \"${TO_SEP}${OPTARG}\")"
+           TO_SEP=", "
            ;;
        --cc|c)
-           ELISP="${ELISP} (message-goto-cc) (insert \"${OPTARG}, \")"
+           ELISP="${ELISP} (message-goto-cc) (insert \"${CC_SEP}${OPTARG}\")"
+           CC_SEP=", "
            ;;
        --bcc|b)
-           ELISP="${ELISP} (message-goto-bcc) (insert \"${OPTARG}, \")"
+           ELISP="${ELISP} (message-goto-bcc) (insert \"${BCC_SEP}${OPTARG}\")"
+           BCC_SEP=", "
            ;;
        --body|i)
            ELISP="${ELISP} (message-goto-body) (insert-file \"${OPTARG}\")"
index 4662e704f5c253ca99d1eb47456571d41faf3392..b6d1e2aefa67d40c03268e8294716846942c2dc0 100644 (file)
 (declare-function notmuch-poll "notmuch-lib" ())
 (declare-function notmuch-tree "notmuch-tree"
                  (&optional query query-context target buffer-name
-                            open-target unthreaded parent-buffer oldest-first))
+                            open-target unthreaded parent-buffer
+                            oldest-first hide-excluded))
 (declare-function notmuch-unthreaded "notmuch-tree"
                  (&optional query query-context target buffer-name
-                            open-target))
+                            open-target oldest-first hide-excluded))
 
 
 ;;; Options
@@ -143,9 +144,13 @@ a plist. Supported properties are
   :sort-order      Specify the sort order to be used for the search.
                    Possible values are `oldest-first', `newest-first'
                    or nil. Nil means use the default sort order.
+  :excluded        Whether to show mail with excluded tags in the
+                   search. Possible values are `hide', `show',
+                   or nil. Nil means use the default value of
+                   `notmuch-search-hide-excluded'.
   :search-type     Specify whether to run the search in search-mode,
                    tree mode or unthreaded mode. Set to `tree' to
-                   specify tree mode, 'unthreaded to specify
+                   specify tree mode, \\='unthreaded to specify
                    unthreaded mode, and set to nil (or anything
                    except tree and unthreaded) to specify search
                    mode.
@@ -484,19 +489,19 @@ diagonal."
             append (notmuch-hello-reflect-generate-row ncols nrows row list))))
 
 (defun notmuch-hello-widget-search (widget &rest _ignore)
-  (cl-case (widget-get widget :notmuch-search-type)
-   (tree
-    (let ((n (notmuch-search-format-buffer-name (widget-value widget) "tree" t)))
-      (notmuch-tree (widget-get widget :notmuch-search-terms)
-                   nil nil n nil nil nil
-                   (widget-get widget :notmuch-search-oldest-first))))
-   (unthreaded
-    (let ((n (notmuch-search-format-buffer-name (widget-value widget)
-                                               "unthreaded" t)))
-      (notmuch-unthreaded (widget-get widget :notmuch-search-terms) nil nil n)))
-   (t
-    (notmuch-search (widget-get widget :notmuch-search-terms)
-                   (widget-get widget :notmuch-search-oldest-first)))))
+  (let ((search-terms (widget-get widget :notmuch-search-terms))
+       (oldest-first (widget-get widget :notmuch-search-oldest-first))
+       (exclude (widget-get widget :notmuch-search-hide-excluded)))
+    (cl-case (widget-get widget :notmuch-search-type)
+      (tree
+       (let ((n (notmuch-search-format-buffer-name (widget-value widget) "tree" t)))
+        (notmuch-tree search-terms nil nil n nil nil nil oldest-first exclude)))
+      (unthreaded
+       (let ((n (notmuch-search-format-buffer-name (widget-value widget)
+                                                  "unthreaded" t)))
+        (notmuch-unthreaded search-terms nil nil n nil oldest-first exclude)))
+      (t
+       (notmuch-search search-terms oldest-first exclude)))))
 
 (defun notmuch-saved-search-count (search)
   (car (notmuch--process-lines notmuch-command "count" search)))
@@ -643,6 +648,10 @@ with `notmuch-hello-query-counts'."
                                     (newest-first nil)
                                     (oldest-first t)
                                     (otherwise notmuch-search-oldest-first)))
+                    (exclude (cl-case (plist-get elem :excluded)
+                               (hide t)
+                               (show nil)
+                               (otherwise notmuch-search-hide-excluded)))
                     (search-type (plist-get elem :search-type))
                     (msg-count (plist-get elem :count)))
                (widget-insert (format "%8s "
@@ -652,6 +661,7 @@ with `notmuch-hello-query-counts'."
                               :notmuch-search-terms query
                               :notmuch-search-oldest-first oldest-first
                               :notmuch-search-type search-type
+                              :notmuch-search-hide-excluded exclude
                               name)
                (setq column-indent
                      (1+ (max 0 (- column-width (length name)))))))
@@ -717,7 +727,8 @@ with `notmuch-hello-query-counts'."
   "Keymap for \"notmuch hello\" buffers.")
 
 (define-derived-mode notmuch-hello-mode fundamental-mode "notmuch-hello"
-  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
+  "Major mode for convenient notmuch navigation. This is your entry
+portal into notmuch.
 
 Saved searches are \"bookmarks\" for arbitrary queries. Hit RET
 or click on a saved search to view matching threads. Edit saved
@@ -853,7 +864,8 @@ Complete list of currently available key bindings:
        (widget-create 'notmuch-search-item :value search :size width)))))
 
 (defun notmuch-hello-insert-searches (title query-list &rest options)
-  "Insert a section with TITLE showing a list of buttons made from QUERY-LIST.
+  "Insert a section with TITLE showing a list of buttons made from
+QUERY-LIST.
 
 QUERY-LIST should ideally be a plist but for backwards
 compatibility other forms are also accepted (see
@@ -867,13 +879,16 @@ Supports the following entries in OPTIONS as a plist:
 :show-empty-searches - show buttons with no matching messages
 :hide-if-empty - hide if no buttons would be shown
    (only makes sense without :show-empty-searches)
-:filter - This can be a function that takes the search query as its argument and
-   returns a filter to be used in conjunction with the query for that search or nil
-   to hide the element. This can also be a string that is used as a combined with
-   each query using \"and\".
-:filter-count - Separate filter to generate the count displayed each search. Accepts
-   the same values as :filter. If :filter and :filter-count are specified, this
-   will be used instead of :filter, not in conjunction with it."
+:filter - This can be a function that takes the search query as
+   its argument and returns a filter to be used in conjunction
+   with the query for that search or nil to hide the
+   element. This can also be a string that is used as a combined
+   with each query using \"and\".
+:filter-count - Separate filter to generate the count displayed
+   each search. Accepts the same values as :filter. If :filter
+   and :filter-count are specified, this will be used instead of
+   :filter, not in conjunction with it."
+
   (widget-insert title ": ")
   (when (and notmuch-hello-first-run (plist-get options :initially-hidden))
     (add-to-list 'notmuch-hello-hidden-sections title))
index 6a2769282ec666190b1a7cdeea8364782389977f..3161ed9526d52d5b8de9a9d08eb3bef626eb38ec 100644 (file)
@@ -50,15 +50,21 @@ fast way to jump to a saved search from anywhere in Notmuch."
                 (cl-case (plist-get saved-search :sort-order)
                   (newest-first nil)
                   (oldest-first t)
-                  (otherwise (default-value 'notmuch-search-oldest-first)))))
+                  (otherwise (default-value 'notmuch-search-oldest-first))))
+               (exclude (cl-case (plist-get saved-search :excluded)
+                          (hide t)
+                          (show nil)
+                          (otherwise notmuch-search-hide-excluded))))
            (push (list key name
                        (cond
                         ((eq (plist-get saved-search :search-type) 'tree)
-                         (lambda () (notmuch-tree query)))
+                         (lambda () (notmuch-tree query nil nil nil nil nil nil
+                                             oldest-first exclude)))
                         ((eq (plist-get saved-search :search-type) 'unthreaded)
-                         (lambda () (notmuch-unthreaded query)))
+                         (lambda () (notmuch-unthreaded query nil nil nil nil
+                                                   oldest-first exclude)))
                         (t
-                         (lambda () (notmuch-search query oldest-first)))))
+                         (lambda () (notmuch-search query oldest-first exclude)))))
                  action-map)))))
     (setq action-map (nreverse action-map))
     (if action-map
index 84ba8c5e56de2aa151682bea74e30a4f3808b2e3..bf9c4a534a24f5d5c088857ce420600604259326 100644 (file)
@@ -105,6 +105,16 @@ search."
   :group 'notmuch-search)
 (make-variable-buffer-local 'notmuch-search-oldest-first)
 
+(defcustom notmuch-search-hide-excluded t
+  "Hide mail tagged with a excluded tag.
+
+Excluded tags are defined in the users configuration file under
+the search section. When this variable is true, any mail with
+such a tag will not be shown in the search output."
+  :type 'boolean
+  :group 'notmuch-search)
+(make-variable-buffer-local 'notmuch-search-hide-excluded)
+
 (defcustom notmuch-poll-script nil
   "[Deprecated] Command to run to incorporate new mail into the notmuch database.
 
@@ -414,9 +424,9 @@ This is similar to `describe-function' for the current major
 mode, but bindings tables are shown with documentation strings
 rather than command names.  By default, this uses the first line
 of each command's documentation string.  A command can override
-this by setting the 'notmuch-doc property of its command symbol.
+this by setting the \\='notmuch-doc property of its command symbol.
 A command that supports a prefix argument can explicitly document
-its prefixed behavior by setting the 'notmuch-prefix-doc property
+its prefixed behavior by setting the \\='notmuch-prefix-doc property
 of its command symbol."
   (interactive)
   (let ((doc (substitute-command-keys
@@ -703,6 +713,7 @@ current buffer, if possible."
          (when (mm-inlinable-p handle)
            (set-buffer display-buffer)
            (mm-display-part handle)
+           (plist-put part :undisplayer (mm-handle-undisplayer handle))
            t))))))
 
 ;;; Generic Utilities
@@ -725,7 +736,7 @@ single element face list."
     (list face)))
 
 (defun notmuch-apply-face (object face &optional below start end)
-  "Combine FACE into the 'face text property of OBJECT between START and END.
+  "Combine FACE into the \\='face text property of OBJECT between START and END.
 
 This function combines FACE with any existing faces between START
 and END in OBJECT.  Attributes specified by FACE take precedence
index ac878a619d07e7c4947f1c359b9d6825580516b6..bf62b65691810f491cb9f98d04e0acc760a4a3ad 100644 (file)
@@ -401,9 +401,10 @@ instead of `message-mode' and SWITCH-FUNCTION is mandatory."
               (addr (and good-tokens (mapconcat #'identity good-tokens ", "))))
          (message-replace-header header addr))))))
 
+;;;#autoload
 (defun notmuch-mua-mail (&optional to subject other-headers _continue
                                   switch-function yank-action send-actions
-                                  return-action &rest ignored)
+                                  return-action &rest _ignored)
   "Invoke the notmuch mail composition window.
 
 The position of point when the function returns differs depending
@@ -416,11 +417,6 @@ moved to the \"To:\" header."
     (let ((user-agent (funcall notmuch-mua-user-agent-function)))
       (unless (string-empty-p user-agent)
        (push (cons 'User-Agent user-agent) other-headers))))
-  (unless (assq 'From other-headers)
-    (push (cons 'From (message-make-from
-                      (notmuch-user-name)
-                      (notmuch-user-primary-email)))
-         other-headers))
   (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)
                             (or switch-function
                                 (notmuch-mua-get-switch-function)))
@@ -439,6 +435,11 @@ moved to the \"To:\" header."
        ;; Cause `message-setup-1' to do things relevant for mail,
        ;; such as observe `message-default-mail-headers'.
        (message-this-is-mail t))
+    (unless (assq 'From headers)
+      (push (cons 'From (message-make-from
+                        (notmuch-user-name)
+                        (notmuch-user-primary-email)))
+           headers))
     (message-setup-1 headers yank-action send-actions return-action))
   (notmuch-fcc-header-setup)
   (notmuch-mua--remove-dont-reply-to-names)
@@ -622,20 +623,24 @@ unencrypted.  Really send? "))))
          (message-send-and-exit arg)
        (message-send arg)))))
 
+;;;#autoload
 (defun notmuch-mua-send-and-exit (&optional arg)
   (interactive "P")
   (notmuch-mua-send-common arg t))
 
+;;;#autoload
 (defun notmuch-mua-send (&optional arg)
   (interactive "P")
   (notmuch-mua-send-common arg))
 
+;;;#autoload
 (defun notmuch-mua-kill-buffer ()
   (interactive)
   (message-kill-buffer))
 
 ;;; _
 
+;;;#autoload
 (define-mail-user-agent 'notmuch-user-agent
   'notmuch-mua-mail
   'notmuch-mua-send-and-exit
index f04b07c2e51b28e34f03469b88205d279c45a587..710c60e15ec12c7b3f5d6ca02d869d5be45f03d6 100644 (file)
@@ -35,7 +35,7 @@ complete S-expression from the input.  However, it extends this
 with an additional function that requires the next value in the
 input to be a list and descends into it, allowing its elements to
 be read one at a time or further descended into.  Both functions
-can return 'retry to indicate that not enough input is available.
+can return \\='retry to indicate that not enough input is available.
 
 The parser always consumes input from point in the current
 buffer.  Hence, the caller is allowed to delete any data before
@@ -52,10 +52,10 @@ point and may resynchronize after an error by moving point."
 (defun notmuch-sexp-read (sp)
   "Consume and return the value at point in the current buffer.
 
-Returns 'retry if there is insufficient input to parse a complete
+Returns \\='retry if there is insufficient input to parse a complete
 value (though it may still move point over whitespace).  If the
 parser is currently inside a list and the next token ends the
-list, this moves point just past the terminator and returns 'end.
+list, this moves point just past the terminator and returns \\='end.
 Otherwise, this moves point to just past the end of the value and
 returns the value."
   (skip-chars-forward " \n\r\t")
@@ -125,7 +125,7 @@ returns the value."
 (defun notmuch-sexp-begin-list (sp)
   "Parse the beginning of a list value and enter the list.
 
-Returns 'retry if there is insufficient input to parse the
+Returns \\='retry if there is insufficient input to parse the
 beginning of the list.  If this is able to parse the beginning of
 a list, it moves point past the token that opens the list and
 returns t.  Later calls to `notmuch-sexp-read' will return the
index 85fa1f218cfae1111cdb3e053c49cd4e05540b38..8d9f1b0830319c5d2bb2262029334d89b5174a10 100644 (file)
 ;;; Utility functions
 
 (defun notmuch-print-run-evince (file)
-  "View FILE using 'evince'."
+  "View FILE using `evince'."
   (start-process "evince" nil "evince" file))
 
 (defun notmuch-print-run-muttprint (&optional output)
-  "Pass the contents of the current buffer to 'muttprint'.
+  "Pass the contents of the current buffer to `muttprint'.
 
 Optional OUTPUT allows passing a list of flags to muttprint."
   (apply #'notmuch--call-process-region (point-min) (point-max)
index ec998ede8dad49ebc1421f7eb839dd24da70cde7..4c0ad74d5043cefcefaa86ab65313660dade6884 100644 (file)
@@ -452,14 +452,19 @@ operation on the contents of the current buffer."
 (defun notmuch-show-update-tags (tags)
   "Update the displayed tags of the current message."
   (save-excursion
-    (goto-char (notmuch-show-message-top))
-    (when (re-search-forward "(\\([^()]*\\))$" (line-end-position) t)
-      (let ((inhibit-read-only t))
-       (replace-match (concat "("
-                              (notmuch-tag-format-tags
-                               tags
-                               (notmuch-show-get-prop :orig-tags))
-                              ")"))))))
+    (let ((inhibit-read-only t)
+         (start (notmuch-show-message-top))
+         (depth (notmuch-show-get-prop :depth))
+         (orig-tags (notmuch-show-get-prop :orig-tags))
+         (props (notmuch-show-get-message-properties))
+         (extent (notmuch-show-message-extent)))
+      (goto-char start)
+      (notmuch-show-insert-headerline props depth tags orig-tags)
+      (put-text-property start (1+ start)
+                        :notmuch-message-properties props)
+      (put-text-property (car extent) (cdr extent) :notmuch-message-extent extent)
+      ;; delete original headerline, but do not save to kill ring
+      (delete-region (point) (1+ (line-end-position))))))
 
 (defun notmuch-clean-address (address)
   "Try to clean a single email ADDRESS for display. Return a cons
@@ -530,11 +535,17 @@ Return unchanged ADDRESS if parsing fails."
          (plist-put msg :height height)
          height))))
 
-(defun notmuch-show-insert-headerline (headers date tags depth duplicate file-count)
+(defun notmuch-show-insert-headerline (msg-plist depth tags &optional orig-tags)
   "Insert a notmuch style headerline based on HEADERS for a
 message at DEPTH in the current thread."
-  (let ((start (point))
-       (from (notmuch-sanitize
+  (let* ((start (point))
+        (headers (plist-get msg-plist :headers))
+        (duplicate (or (plist-get msg-plist :duplicate) 0))
+        (file-count (length (plist-get msg-plist :filename)))
+        (date (or (and notmuch-show-relative-dates
+                       (plist-get msg-plist :date_relative))
+                  (plist-get headers :Date)))
+        (from (notmuch-sanitize
               (notmuch-show-clean-address (plist-get headers :From)))))
     (when (string-match "\\cR" from)
       ;; If the From header has a right-to-left character add
@@ -549,7 +560,7 @@ message at DEPTH in the current thread."
            " ("
            date
            ") ("
-           (notmuch-tag-format-tags tags tags)
+           (notmuch-tag-format-tags tags (or orig-tags tags))
            ")")
     (insert
      (if (> file-count 1)
@@ -633,8 +644,24 @@ message at DEPTH in the current thread."
                (when show
                  (button-put button :notmuch-lazy-part nil)
                  (notmuch-show-lazy-part lazy-part button))
-             ;; else there must be an overlay.
-             (overlay-put overlay 'invisible (not show))
+             (let* ((part (plist-get properties :notmuch-part))
+                    (undisplayer (plist-get part :undisplayer))
+                    (mime-type (plist-get part :computed-type))
+                    (redisplay-data (button-get button
+                                                :notmuch-redisplay-data))
+                    (imagep (string-match "^image/" mime-type)))
+               (cond
+                ((and imagep (not show) undisplayer)
+                 ;; call undisplayer thunk created by gnus.
+                 (funcall undisplayer)
+                 ;; there is an extra newline left
+                 (delete-region
+                  (+ 1 (button-end button))
+                  (+ 2 (button-end button))))
+                ((and imagep show redisplay-data)
+                 (notmuch-show-lazy-part redisplay-data button))
+                (t
+                 (overlay-put overlay 'invisible (not show)))))
              t)))))))
 
 ;;; Part content ID handling
@@ -1008,10 +1035,13 @@ will return nil if the CID is unknown or cannot be retrieved."
           (part-end (copy-marker (point) t))
           ;; We have to save the depth as we can't find the depth
           ;; when narrowed.
-          (depth (notmuch-show-get-depth)))
+          (depth (notmuch-show-get-depth))
+          (mime-type (plist-get (cadr part-args) :computed-type)))
       (save-restriction
        (narrow-to-region part-beg part-end)
        (delete-region part-beg part-end)
+       (when (and mime-type (string-match "^image/" mime-type))
+         (button-put button :notmuch-redisplay-data part-args))
        (apply #'notmuch-show-insert-bodypart-internal part-args)
        (indent-rigidly part-beg
                        part-end
@@ -1095,14 +1125,18 @@ is t, hide the part initially and show the button."
                             (and deep button)
                             (and high button)
                             (and long button))))
-        (content-beg (point)))
+        (content-beg (point))
+        (part-data (list msg part mime-type nth depth button)))
     ;; Store the computed mime-type for later use (e.g. by attachment handlers).
     (plist-put part :computed-type mime-type)
-    (if show-part
-       (notmuch-show-insert-bodypart-internal msg part mime-type nth depth button)
+    (cond
+     (show-part
+      (apply #'notmuch-show-insert-bodypart-internal part-data)
+      (when (and button (string-match "^image/" mime-type))
+       (button-put button :notmuch-redisplay-data part-data)))
+     (t
       (when button
-       (button-put button :notmuch-lazy-part
-                   (list msg part mime-type nth depth button))))
+       (button-put button :notmuch-lazy-part part-data))))
     ;; Some of the body part handlers leave point somewhere up in the
     ;; part, so we make sure that we're down at the end.
     (goto-char (point-max))
@@ -1171,8 +1205,6 @@ is out of range."
 (defun notmuch-show-insert-msg (msg depth)
   "Insert the message MSG at depth DEPTH in the current thread."
   (let* ((headers (plist-get msg :headers))
-        (duplicate (or (plist-get msg :duplicate) 0))
-        (files (length (plist-get msg :filename)))
         ;; Indentation causes the buffer offset of the start/end
         ;; points to move, so we must use markers.
         message-start message-end
@@ -1180,11 +1212,7 @@ is out of range."
         headers-start headers-end
         (bare-subject (notmuch-show-strip-re (plist-get headers :Subject))))
     (setq message-start (point-marker))
-    (notmuch-show-insert-headerline headers
-                                   (or (and notmuch-show-relative-dates
-                                            (plist-get msg :date_relative))
-                                       (plist-get headers :Date))
-                                   (plist-get msg :tags) depth duplicate files)
+    (notmuch-show-insert-headerline msg depth (plist-get msg :tags))
     (setq content-start (point-marker))
     ;; Set `headers-start' to point after the 'Subject:' header to be
     ;; compatible with the existing implementation. This just sets it
@@ -2479,10 +2507,12 @@ kill-ring."
 (defun notmuch-show-stash-mlarchive-link (&optional mla)
   "Copy an ML Archive URI for the current message to the kill-ring.
 
-This presumes that the message is available at the selected Mailing List Archive.
+This presumes that the message is available at the selected
+Mailing List Archive.
 
-If optional argument MLA is non-nil, use the provided key instead of prompting
-the user (see `notmuch-show-stash-mlarchive-link-alist')."
+If optional argument MLA is non-nil, use the provided key instead
+of prompting the user (see
+`notmuch-show-stash-mlarchive-link-alist')."
   (interactive)
   (let ((url (cdr (assoc
                   (or mla
@@ -2499,12 +2529,15 @@ the user (see `notmuch-show-stash-mlarchive-link-alist')."
        (concat url (notmuch-show-get-message-id t))))))
 
 (defun notmuch-show-stash-mlarchive-link-and-go (&optional mla)
-  "Copy an ML Archive URI for the current message to the kill-ring and visit it.
+  "Copy an ML Archive URI for the current message to the
+ kill-ring and visit it.
 
-This presumes that the message is available at the selected Mailing List Archive.
+This presumes that the message is available at the selected
+Mailing List Archive.
 
-If optional argument MLA is non-nil, use the provided key instead of prompting
-the user (see `notmuch-show-stash-mlarchive-link-alist')."
+If optional argument MLA is non-nil, use the provided key instead
+of prompting the user (see
+`notmuch-show-stash-mlarchive-link-alist')."
   (interactive)
   (notmuch-show-stash-mlarchive-link mla)
   (browse-url (current-kill 0 t)))
index 959778819617f9cd23498818b2344103fb008c12..811018288f373b1df099fa7b791b27a47e313709 100644 (file)
@@ -159,7 +159,7 @@ place of the tag.
 For example, to replace a tag with another string, simply use
 that string as a formatting expression.  To change the foreground
 of a tag to red, use the expression
-  (propertize tag 'face '(:foreground \"red\"))
+  (propertize tag \\='face \\='(:foreground \"red\"))
 
 See also `notmuch-tag-format-image', which can help replace tags
 with images."
@@ -191,7 +191,7 @@ By default this shows deleted tags with strike-through in red,
 unless strike-through is not available (e.g., emacs is running in
 a terminal) in which case it uses inverse video. To hide deleted
 tags completely set this to
-  '((\".*\" nil))
+  \\='((\".*\" nil))
 
 See `notmuch-tag-formats' for full documentation."
   :group 'notmuch-show
@@ -366,9 +366,9 @@ changed (the normal case) are shown using formats from
 (defcustom notmuch-before-tag-hook nil
   "Hooks that are run before tags of a message are modified.
 
-'tag-changes' will contain the tags that are about to be added or removed as
+`tag-changes' will contain the tags that are about to be added or removed as
 a list of strings of the form \"+TAG\" or \"-TAG\".
-'query' will be a string containing the search query that determines
+`query' will be a string containing the search query that determines
 the messages that are about to be tagged."
   :type 'hook
   :options '(notmuch-hl-line-mode)
@@ -377,9 +377,9 @@ the messages that are about to be tagged."
 (defcustom notmuch-after-tag-hook nil
   "Hooks that are run after tags of a message are modified.
 
-'tag-changes' will contain the tags that were added or removed as
+`tag-changes' will contain the tags that were added or removed as
 a list of strings of the form \"+TAG\" or \"-TAG\".
-'query' will be a string containing the search query that determines
+`query' will be a string containing the search query that determines
 the messages that were tagged."
   :type 'hook
   :options '(notmuch-hl-line-mode)
index 7ceddee22fc49615952469d759595fcb4059e372..faec89c48a9a456a294d1202f240a17dde136ffa 100644 (file)
@@ -45,9 +45,6 @@
 (declare-function notmuch-search-previous-thread "notmuch" ())
 (declare-function notmuch-tree-from-search-thread "notmuch" ())
 
-;; the following variable is defined in notmuch.el
-(defvar notmuch-search-query-string)
-
 ;; this variable distinguishes the unthreaded display from the normal tree display
 (defvar-local notmuch-tree-unthreaded nil
   "A buffer local copy of argument unthreaded to the function notmuch-tree.")
@@ -203,7 +200,8 @@ Note that the author string should not contain whitespace
 
 (defface notmuch-tree-match-tree-face
   nil
-  "Face used in tree mode for the thread tree block graphics in messages matching the query."
+  "Face used in tree mode for the thread tree block graphics in
+messages matching the query."
   :group 'notmuch-tree
   :group 'notmuch-faces)
 
@@ -242,7 +240,8 @@ Note that the author string should not contain whitespace
 
 (defface notmuch-tree-no-match-tree-face
   nil
-  "Face used in tree mode for the thread tree block graphics in messages matching the query."
+  "Face used in tree mode for the thread tree block graphics in
+messages matching the query."
   :group 'notmuch-tree
   :group 'notmuch-faces)
 
@@ -375,6 +374,7 @@ then NAME behaves like CMD."
     (define-key map [remap notmuch-jump-search]   'notmuch-tree-jump-search)
 
     (define-key map "o" 'notmuch-tree-toggle-order)
+    (define-key map "i" 'notmuch-tree-toggle-hide-excluded)
     (define-key map "S" 'notmuch-search-from-tree-current-query)
     (define-key map "U" 'notmuch-unthreaded-from-tree-current-query)
     (define-key map "Z" 'notmuch-tree-from-unthreaded-current-query)
@@ -399,6 +399,7 @@ then NAME behaves like CMD."
     (define-key map "V" 'notmuch-tree-view-raw-message)
     (define-key map "l" 'notmuch-tree-filter)
     (define-key map "t" 'notmuch-tree-filter-by-tag)
+    (define-key map "E" 'notmuch-tree-edit-search)
 
     ;; The main tree view bindings
     (define-key map (kbd "RET") 'notmuch-tree-show-message)
@@ -590,7 +591,9 @@ NOT change the database."
   "Call notmuch search with the current query."
   (interactive)
   (notmuch-tree-close-message-window)
-  (notmuch-search (notmuch-tree-get-query)))
+  (notmuch-search (notmuch-tree-get-query)
+                 notmuch-search-oldest-first
+                 notmuch-search-hide-excluded))
 
 (defun notmuch-tree-message-window-kill-hook ()
   "Close the message pane when exiting the show buffer."
@@ -803,7 +806,8 @@ nil otherwise."
                         target
                         nil
                         unthreaded
-                        notmuch-search-oldest-first)))
+                        notmuch-search-oldest-first
+                        notmuch-search-hide-excluded)))
 
 (defun notmuch-tree-thread-top ()
   (when (notmuch-tree-get-message-properties)
@@ -1016,7 +1020,10 @@ unchanged ADDRESS if parsing fails."
 A message tree is another name for a single sub-thread: i.e., a
 message together with all its descendents."
   (let ((msg (car tree))
-       (replies (cadr tree)))
+       (replies (cadr tree))
+       ;; outline level, computed from the message's depth and
+       ;; whether or not it's the first message in the tree.
+       (level (1+ (if (and (eq 0 depth) (not first)) 1 depth))))
     (cond
      ((and (< 0 depth) (not last))
       (push (alist-get 'vertical-tee  notmuch-tree-thread-symbols) tree-status))
@@ -1036,6 +1043,7 @@ message together with all its descendents."
     (setq msg (plist-put msg :first (and first (eq 0 depth))))
     (setq msg (plist-put msg :tree-status tree-status))
     (setq msg (plist-put msg :orig-tags (plist-get msg :tags)))
+    (setq msg (plist-put msg :level level))
     (notmuch-tree-goto-and-insert-msg msg)
     (pop tree-status)
     (pop tree-status)
@@ -1045,7 +1053,8 @@ message together with all its descendents."
     (notmuch-tree-insert-thread replies (1+ depth) tree-status)))
 
 (defun notmuch-tree-insert-thread (thread depth tree-status)
-  "Insert the collection of sibling sub-threads THREAD at depth DEPTH in the current forest."
+  "Insert the collection of sibling sub-threads THREAD at depth
+DEPTH in the current forest."
   (let ((n (length thread)))
     (cl-loop for tree in thread
             for count from 1 to n
@@ -1082,7 +1091,8 @@ Complete list of currently available key bindings:
   (setq notmuch-buffer-refresh-function #'notmuch-tree-refresh-view)
   (hl-line-mode 1)
   (setq buffer-read-only t)
-  (setq truncate-lines t))
+  (setq truncate-lines t)
+  (when notmuch-tree-outline-enabled (notmuch-tree-outline-mode 1)))
 
 (defvar notmuch-tree-process-exit-functions nil
   "Functions called when the process inserting a tree of results finishes.
@@ -1127,7 +1137,8 @@ object, and with the tree results buffer as the current buffer.")
                                         results-buf)))))
 
 (defun notmuch-tree-worker (basic-query &optional query-context target
-                                       open-target unthreaded oldest-first)
+                                       open-target unthreaded oldest-first
+                                       exclude)
   "Insert the tree view of the search in the current buffer.
 
 This is is a helper function for notmuch-tree. The arguments are
@@ -1136,6 +1147,7 @@ the same as for the function notmuch-tree."
   (notmuch-tree-mode)
   (add-hook 'post-command-hook #'notmuch-tree-command-hook t t)
   (setq notmuch-search-oldest-first oldest-first)
+  (setq notmuch-search-hide-excluded exclude)
   (setq notmuch-tree-unthreaded unthreaded)
   (setq notmuch-tree-basic-query basic-query)
   (setq notmuch-tree-query-context (if (or (string= query-context "")
@@ -1155,14 +1167,15 @@ the same as for the function notmuch-tree."
                              (and query-context
                                   (concat " and (" query-context ")"))))
         (sort-arg (if oldest-first "--sort=oldest-first" "--sort=newest-first"))
-        (message-arg (if unthreaded "--unthreaded" "--entire-thread")))
+        (message-arg (if unthreaded "--unthreaded" "--entire-thread"))
+        (exclude-arg (if exclude "--exclude=true" "--exclude=false")))
     (when (equal (car (notmuch--process-lines notmuch-command "count" search-args)) "0")
       (setq search-args basic-query))
     (notmuch-tag-clear-cache)
     (let ((proc (notmuch-start-notmuch
                 "notmuch-tree" (current-buffer) #'notmuch-tree-process-sentinel
                 "show" "--body=false" "--format=sexp" "--format-version=5"
-                sort-arg message-arg search-args))
+                sort-arg message-arg exclude-arg search-args))
          ;; Use a scratch buffer to accumulate partial output.
          ;; This buffer will be killed by the sentinel, which
          ;; should be called no matter how the process dies.
@@ -1189,8 +1202,19 @@ default sort order is defined by `notmuch-search-oldest-first'."
   (setq notmuch-search-oldest-first (not notmuch-search-oldest-first))
   (notmuch-tree-refresh-view))
 
+(defun notmuch-tree-toggle-hide-excluded ()
+  "Toggle whether to hide excluded messages.
+
+This command toggles whether to hide excluded messages for the current
+search. The default value for this is defined by `notmuch-search-hide-excluded'."
+  (interactive)
+  (setq notmuch-search-hide-excluded (not notmuch-search-hide-excluded))
+  (notmuch-tree-refresh-view))
+
+;;;###autoload
 (defun notmuch-tree (&optional query query-context target buffer-name
-                              open-target unthreaded parent-buffer oldest-first)
+                              open-target unthreaded parent-buffer
+                              oldest-first hide-excluded)
   "Display threads matching QUERY in tree view.
 
 The arguments are:
@@ -1205,7 +1229,15 @@ The arguments are:
       it is nil \"*notmuch-tree\" followed by QUERY is used.
   OPEN-TARGET: If TRUE open the target message in the message pane.
   UNTHREADED: If TRUE only show matching messages in an unthreaded view."
-  (interactive)
+  (interactive
+   (list
+    ;; Prompt for a query
+    nil
+    ;; Fill other args with nil.
+    nil nil nil nil nil nil
+    ;; Populate these from the default value of these options.
+    (default-value 'notmuch-search-oldest-first)
+    (default-value 'notmuch-search-hide-excluded)))
   (unless query
     (setq query (notmuch-read-query (concat "Notmuch "
                                            (if unthreaded "unthreaded " "tree ")
@@ -1219,17 +1251,27 @@ The arguments are:
     (pop-to-buffer-same-window buffer))
   ;; Don't track undo information for this buffer
   (setq buffer-undo-list t)
-  (notmuch-tree-worker query query-context target open-target unthreaded oldest-first)
+  (notmuch-tree-worker query query-context target open-target
+                      unthreaded oldest-first hide-excluded)
   (setq notmuch-tree-parent-buffer parent-buffer)
   (setq truncate-lines t))
 
 (defun notmuch-unthreaded (&optional query query-context target buffer-name
-                                    open-target)
+                                    open-target oldest-first hide-excluded)
   "Display threads matching QUERY in unthreaded view.
 
 See function NOTMUCH-TREE for documentation of the arguments"
-  (interactive)
-  (notmuch-tree query query-context target buffer-name open-target t))
+  (interactive
+   (list
+    ;; Prompt for a query
+    nil
+    ;; Fill other args with nil.
+    nil nil nil nil
+    ;; Populate these from the default value of these options.
+    (default-value 'notmuch-search-oldest-first)
+    (default-value 'notmuch-search-hide-excluded)))
+  (notmuch-tree query query-context target buffer-name open-target
+               t nil oldest-first hide-excluded))
 
 (defun notmuch-tree-filter (query)
   "Filter or LIMIT the current search results based on an additional query string.
@@ -1257,6 +1299,22 @@ search results and that are also tagged with the given TAG."
   (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto)))
     (notmuch-tree-close-message-window)
     (notmuch-tree (concat notmuch-tree-basic-query " and tag:" tag)
+                 notmuch-tree-query-context
+                 nil
+                 nil
+                 nil
+                 notmuch-tree-unthreaded
+                 nil
+                 notmuch-search-oldest-first
+                 notmuch-search-hide-excluded)))
+
+(defun notmuch-tree-edit-search (query)
+  "Edit the current search"
+  (interactive (list (read-from-minibuffer "Edit search: "
+                                          notmuch-tree-basic-query)))
+  (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto)))
+    (notmuch-tree-close-message-window)
+    (notmuch-tree query
                  notmuch-tree-query-context
                  nil
                  nil
@@ -1265,6 +1323,180 @@ search results and that are also tagged with the given TAG."
                  nil
                  notmuch-search-oldest-first)))
 
+;;; Tree outline mode
+;;;; Custom variables
+(defcustom notmuch-tree-outline-enabled nil
+  "Whether to automatically activate `notmuch-tree-outline-mode' in tree views."
+  :type 'boolean)
+
+(defcustom notmuch-tree-outline-visibility 'hide-others
+  "Default state of the forest outline for `notmuch-tree-outline-mode'.
+
+This variable controls the state of a forest initially and after
+a movement command.  If set to nil, all trees are displayed while
+the symbol hide-all indicates that all trees in the forest should
+be folded and hide-other that only the first one should be
+unfolded."
+  :type '(choice (const :tag "Show all" nil)
+                (const :tag "Hide others" hide-others)
+                (const :tag "Hide all" hide-all)))
+
+(defcustom notmuch-tree-outline-auto-close nil
+  "Close message and tree windows when moving past the last message."
+  :type 'boolean)
+
+(defcustom notmuch-tree-outline-open-on-next nil
+  "Open new messages under point if they are closed when moving to next one.
+
+When this flag is set, using the command
+`notmuch-tree-outline-next' with point on a header for a new
+message that is not shown will open its `notmuch-show' buffer
+instead of moving point to next matching message."
+  :type 'boolean)
+
+;;;; Helper functions
+(defsubst notmuch-tree-outline--pop-at-end (pop-at-end)
+  (if notmuch-tree-outline-auto-close (not pop-at-end) pop-at-end))
+
+(defun notmuch-tree-outline--set-visibility ()
+  (when (and notmuch-tree-outline-mode (> (point-max) (point-min)))
+    (cl-case notmuch-tree-outline-visibility
+      (hide-others (notmuch-tree-outline-hide-others))
+      (hide-all (outline-hide-body)))))
+
+(defun notmuch-tree-outline--on-exit (proc)
+  (when (eq (process-status proc) 'exit)
+    (notmuch-tree-outline--set-visibility)))
+
+(add-hook 'notmuch-tree-process-exit-functions #'notmuch-tree-outline--on-exit)
+
+(defsubst notmuch-tree-outline--level (&optional props)
+  (or (plist-get (or props (notmuch-tree-get-message-properties)) :level) 0))
+
+(defsubst notmuch-tree-outline--message-open-p ()
+  (and (buffer-live-p notmuch-tree-message-buffer)
+       (get-buffer-window notmuch-tree-message-buffer)
+       (let ((id (notmuch-tree-get-message-id)))
+        (and id
+             (with-current-buffer notmuch-tree-message-buffer
+               (string= (notmuch-show-get-message-id) id))))))
+
+(defsubst notmuch-tree-outline--at-original-match-p ()
+  (and (notmuch-tree-get-prop :match)
+       (equal (notmuch-tree-get-prop :orig-tags)
+              (notmuch-tree-get-prop :tags))))
+
+(defun notmuch-tree-outline--next (prev thread pop-at-end &optional open-new)
+  (cond (thread
+        (notmuch-tree-thread-top)
+        (if prev
+            (outline-backward-same-level 1)
+          (outline-forward-same-level 1))
+        (when (> (notmuch-tree-outline--level) 0) (outline-show-branches))
+        (notmuch-tree-outline--next nil nil pop-at-end t))
+       ((and (or open-new notmuch-tree-outline-open-on-next)
+             (notmuch-tree-outline--at-original-match-p)
+             (not (notmuch-tree-outline--message-open-p)))
+        (notmuch-tree-outline-hide-others t))
+       (t (outline-next-visible-heading (if prev -1 1))
+          (unless (notmuch-tree-get-prop :match)
+            (notmuch-tree-matching-message prev pop-at-end))
+          (notmuch-tree-outline-hide-others t))))
+
+;;;; User commands
+(defun notmuch-tree-outline-hide-others (&optional and-show)
+  "Fold all threads except the one around point.
+If AND-SHOW is t, make the current message visible if it's not."
+  (interactive)
+  (save-excursion
+    (while (and (not (bobp)) (> (notmuch-tree-outline--level) 1))
+      (outline-previous-heading))
+    (outline-hide-sublevels 1))
+  (when (> (notmuch-tree-outline--level) 0)
+    (outline-show-subtree)
+    (when and-show (notmuch-tree-show-message nil))))
+
+(defun notmuch-tree-outline-next (&optional pop-at-end)
+  "Next matching message in a forest, taking care of thread visibility.
+A prefix argument reverses the meaning of `notmuch-tree-outline-auto-close'."
+  (interactive "P")
+  (let ((pop (notmuch-tree-outline--pop-at-end pop-at-end)))
+    (if (null notmuch-tree-outline-visibility)
+       (notmuch-tree-matching-message nil pop)
+      (notmuch-tree-outline--next nil nil pop))))
+
+(defun notmuch-tree-outline-previous (&optional pop-at-end)
+  "Previous matching message in forest, taking care of thread visibility.
+With prefix, quit the tree view if there is no previous message."
+  (interactive "P")
+  (if (null notmuch-tree-outline-visibility)
+      (notmuch-tree-prev-matching-message pop-at-end)
+    (notmuch-tree-outline--next t nil pop-at-end)))
+
+(defun notmuch-tree-outline-next-thread ()
+  "Next matching thread in forest, taking care of thread visibility."
+  (interactive)
+  (if (null notmuch-tree-outline-visibility)
+      (notmuch-tree-next-thread)
+    (notmuch-tree-outline--next nil t nil)))
+
+(defun notmuch-tree-outline-previous-thread ()
+  "Previous matching thread in forest, taking care of thread visibility."
+  (interactive)
+  (if (null notmuch-tree-outline-visibility)
+      (notmuch-tree-prev-thread)
+    (notmuch-tree-outline--next t t nil)))
+
+;;;; Mode definition
+(defvar notmuch-tree-outline-mode-lighter nil
+  "The lighter mark for notmuch-tree-outline mode.
+Usually empty since outline-minor-mode's lighter will be active.")
+
+(define-minor-mode notmuch-tree-outline-mode
+  "Minor mode allowing message trees to be folded as outlines.
+
+When this mode is set, each thread and subthread in the results
+list is treated as a foldable section, with its first message as
+its header.
+
+The mode just makes available in the tree buffer all the
+keybindings in `outline-minor-mode', and binds the following
+additional keys:
+
+\\{notmuch-tree-outline-mode-map}
+
+The customizable variable `notmuch-tree-outline-visibility'
+controls how navigation in the buffer is affected by this mode:
+
+  - If it is set to nil, `notmuch-tree-outline-previous',
+    `notmuch-tree-outline-next', and their thread counterparts
+    behave just as the corresponding notmuch-tree navigation keys
+    when this mode is not enabled.
+
+  - If, on the other hand, `notmuch-tree-outline-visibility' is
+    set to a non-nil value, these commands hiding the outlines of
+    the trees you are not reading as you move to new messages.
+
+To enable notmuch-tree-outline-mode by default in all
+notmuch-tree buffers, just set
+`notmuch-tree-outline-mode-enabled' to t."
+  :lighter notmuch-tree-outline-mode-lighter
+  :keymap `((,(kbd "TAB") . outline-cycle)
+           (,(kbd "M-TAB") . outline-cycle-buffer)
+           ("n" . notmuch-tree-outline-next)
+           ("p" . notmuch-tree-outline-previous)
+           (,(kbd "M-n") . notmuch-tree-outline-next-thread)
+           (,(kbd "M-p") . notmuch-tree-outline-previous-thread))
+  (outline-minor-mode notmuch-tree-outline-mode)
+  (unless (derived-mode-p 'notmuch-tree-mode)
+    (user-error "notmuch-tree-outline-mode is only meaningful for notmuch trees!"))
+  (if notmuch-tree-outline-mode
+      (progn (setq-local outline-regexp "^[^\n]+")
+            (setq-local outline-level #'notmuch-tree-outline--level)
+            (notmuch-tree-outline--set-visibility))
+    (setq-local outline-regexp (default-value 'outline-regexp))
+    (setq-local        outline-level (default-value 'outline-level))))
+
 ;;; _
 
 (provide 'notmuch-tree)
index 653ecc2ae7d5d4e07ff8a87c9ed95315fb3c30bb..fd8a9d1e2296db96e6cf6642bbe0ba35819942ae 100644 (file)
@@ -381,7 +381,7 @@ original filename the sender had."
          (notmuch-wash-subject-to-filename subject 52)))
 
 (defun notmuch-wash-convert-inline-patch-to-part (msg depth)
-  "Convert an inline patch into a fake 'text/x-diff' attachment.
+  "Convert an inline patch into a fake `text/x-diff' attachment.
 
 Given that this function guesses whether a buffer includes a
 patch and then guesses the extent of the patch, there is scope
index 5cb7acd298ae64899d8f7fcf4513627fdcf556cb..2a73ffa571c41a04b0836a50a991102249fad658 100644 (file)
@@ -187,9 +187,11 @@ there will be called at other points of notmuch execution."
     (define-key map "r" 'notmuch-search-reply-to-thread-sender)
     (define-key map "R" 'notmuch-search-reply-to-thread)
     (define-key map "o" 'notmuch-search-toggle-order)
+    (define-key map "i" 'notmuch-search-toggle-hide-excluded)
     (define-key map "c" 'notmuch-search-stash-map)
     (define-key map "t" 'notmuch-search-filter-by-tag)
     (define-key map "l" 'notmuch-search-filter)
+    (define-key map "E" 'notmuch-search-edit-search)
     (define-key map [mouse-1] 'notmuch-search-show-thread)
     (define-key map "k" 'notmuch-tag-jump)
     (define-key map "*" 'notmuch-search-tag-all)
@@ -549,12 +551,18 @@ Return non-nil on success."
 (defun notmuch-tree-from-search-current-query ()
   "Tree view of current query."
   (interactive)
-  (notmuch-tree notmuch-search-query-string))
+  (notmuch-tree notmuch-search-query-string
+               nil nil nil nil nil nil
+               notmuch-search-oldest-first
+               notmuch-search-hide-excluded))
 
 (defun notmuch-unthreaded-from-search-current-query ()
   "Unthreaded view of current query."
   (interactive)
-  (notmuch-unthreaded notmuch-search-query-string))
+  (notmuch-unthreaded notmuch-search-query-string
+                     nil nil nil nil
+                     notmuch-search-oldest-first
+                     notmuch-search-hide-excluded))
 
 (defun notmuch-tree-from-search-thread ()
   "Show the selected thread with notmuch-tree."
@@ -563,7 +571,9 @@ Return non-nil on success."
                notmuch-search-query-string
                nil
                (notmuch-prettify-subject (notmuch-search-find-subject))
-               t nil (current-buffer)))
+               t nil (current-buffer)
+               notmuch-search-oldest-first
+               notmuch-search-hide-excluded))
 
 (defun notmuch-search-reply-to-thread (&optional prompt-for-sender)
   "Begin composing a reply-all to the entire current thread in a new buffer."
@@ -840,6 +850,7 @@ non-authors is found, assume that all of the authors match."
              overlay)
          (insert invisible-string)
          (setq overlay (make-overlay start (point)))
+         (overlay-put overlay 'evaporate t)
          (overlay-put overlay 'invisible 'ellipsis)
          (overlay-put overlay 'isearch-open-invisible #'delete-overlay)))
       (insert padding))))
@@ -1033,14 +1044,16 @@ PROMPT is the string to prompt with."
 
 (put 'notmuch-search 'notmuch-doc "Search for messages.")
 ;;;###autoload
-(defun notmuch-search (&optional query oldest-first target-thread target-line
-                                no-display)
+(defun notmuch-search (&optional query oldest-first hide-excluded target-thread
+                                target-line no-display)
   "Display threads matching QUERY in a notmuch-search buffer.
 
 If QUERY is nil, it is read interactively from the minibuffer.
 Other optional parameters are used as follows:
 
   OLDEST-FIRST: A Boolean controlling the sort order of returned threads
+  HIDE-EXCLUDED: A boolean controlling whether to omit threads with excluded
+                 tags.
   TARGET-THREAD: A thread ID (without the thread: prefix) that will be made
                  current if it appears in the search results.
   TARGET-LINE: The line number to move to if the target thread does not
@@ -1055,9 +1068,10 @@ the configured default sort order."
    (list
     ;; Prompt for a query
     nil
-    ;; Use the default search order (if we're doing a search from a
-    ;; search buffer, ignore any buffer-local overrides)
-    (default-value 'notmuch-search-oldest-first)))
+    ;; Use the default search order and exclude value (if we're doing a
+    ;; search from a search buffer, ignore any buffer-local overrides)
+    (default-value 'notmuch-search-oldest-first)
+    (default-value 'notmuch-search-hide-excluded)))
 
   (let* ((query (or query (notmuch-read-query "Notmuch search: ")))
         (buffer (get-buffer-create (notmuch-search-buffer-title query))))
@@ -1071,6 +1085,7 @@ the configured default sort order."
     (setq notmuch-search-oldest-first oldest-first)
     (setq notmuch-search-target-thread target-thread)
     (setq notmuch-search-target-line target-line)
+    (setq notmuch-search-hide-excluded hide-excluded)
     (notmuch-tag-clear-cache)
     (when (get-buffer-process buffer)
       (error "notmuch search process already running for query `%s'" query))
@@ -1084,6 +1099,9 @@ the configured default sort order."
                     (if oldest-first
                         "--sort=oldest-first"
                       "--sort=newest-first")
+                    (if hide-excluded
+                        "--exclude=true"
+                      "--exclude=false")
                     query)))
          ;; Use a scratch buffer to accumulate partial output.
          ;; This buffer will be killed by the sentinel, which
@@ -1104,11 +1122,21 @@ same relative position within the new buffer."
   (interactive)
   (notmuch-search notmuch-search-query-string
                  notmuch-search-oldest-first
+                 notmuch-search-hide-excluded
                  (notmuch-search-find-thread-id 'bare)
                  (line-number-at-pos)
                  t)
   (goto-char (point-min)))
 
+(defun notmuch-search-toggle-hide-excluded ()
+  "Toggle whether to hide excluded messages.
+
+This command toggles whether to hide excluded messages for the current
+search. The default value for this is defined by `notmuch-search-hide-excluded'."
+  (interactive)
+  (setq notmuch-search-hide-excluded (not notmuch-search-hide-excluded))
+  (notmuch-search-refresh-view))
+
 (defun notmuch-search-toggle-order ()
   "Toggle the current search order.
 
@@ -1137,7 +1165,8 @@ current search results AND the additional query string provided."
     (notmuch-search (if (string= grouped-original-query "*")
                        grouped-query
                      (concat grouped-original-query " and " grouped-query))
-                   notmuch-search-oldest-first)))
+                   notmuch-search-oldest-first
+                   notmuch-search-hide-excluded)))
 
 (defun notmuch-search-filter-by-tag (tag)
   "Filter the current search results based on a single TAG.
@@ -1148,13 +1177,22 @@ search results and that are also tagged with the given TAG."
    (list (notmuch-select-tag-with-completion "Filter by tag: "
                                             notmuch-search-query-string)))
   (notmuch-search (concat notmuch-search-query-string " and tag:" tag)
-                 notmuch-search-oldest-first))
+                 notmuch-search-oldest-first
+                 notmuch-search-hide-excluded))
 
 (defun notmuch-search-by-tag (tag)
   "Display threads matching TAG in a notmuch-search buffer."
   (interactive
    (list (notmuch-select-tag-with-completion "Notmuch search tag: ")))
-  (notmuch-search (concat "tag:" tag)))
+  (notmuch-search (concat "tag:" tag)
+                 (default-value 'notmuch-search-oldest-first)
+                 (default-value 'notmuch-search-hide-excluded)))
+
+(defun notmuch-search-edit-search (query)
+  "Edit the current search"
+  (interactive (list (read-from-minibuffer "Edit search: "
+                                          notmuch-search-query-string)))
+  (notmuch-search query notmuch-search-oldest-first))
 
 ;;;###autoload
 (defun notmuch ()
index 503a0c8b585323dd59fad974b55ca47b7399fad6..6cd15fab26bc16610e1c56e1b5e0019b41279259 100644 (file)
@@ -416,7 +416,8 @@ _expand_path (void *ctx, const char *key, const char *val)
 
 notmuch_status_t
 _notmuch_config_load_from_file (notmuch_database_t *notmuch,
-                               GKeyFile *file)
+                               GKeyFile *file,
+                               char **status_string)
 {
     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
     gchar **groups = NULL, **keys, *val;
@@ -435,6 +436,7 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
        for (gchar **keys_p = keys; *keys_p; keys_p++) {
            char *absolute_key = talloc_asprintf (notmuch, "%s.%s", *grp,  *keys_p);
            char *normalized_val;
+           GError *gerr = NULL;
 
            /* If we opened from a given path, do not overwrite it */
            if (strcmp (absolute_key, "database.path") == 0 &&
@@ -442,7 +444,14 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
                notmuch->xapian_db)
                continue;
 
-           val = g_key_file_get_string (file, *grp, *keys_p, NULL);
+           val = g_key_file_get_string (file, *grp, *keys_p, &gerr);
+           if (gerr) {
+               if (status_string)
+                   IGNORE_RESULT (asprintf (status_string,
+                                            "GLib: %s\n",
+                                            gerr->message));
+               g_error_free (gerr);
+           }
            if (! val) {
                status = NOTMUCH_STATUS_FILE_ERROR;
                goto DONE;
@@ -599,6 +608,8 @@ _notmuch_config_key_to_string (notmuch_config_key_t key)
        return "database.autocommit";
     case NOTMUCH_CONFIG_EXTRA_HEADERS:
        return "show.extra_headers";
+    case NOTMUCH_CONFIG_INDEX_AS_TEXT:
+       return "index.as_text";
     default:
        return NULL;
     }
@@ -642,6 +653,7 @@ _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
        else
            email = _get_email_from_passwd_file (notmuch);
        return email;
+    case NOTMUCH_CONFIG_INDEX_AS_TEXT:
     case NOTMUCH_CONFIG_NEW_IGNORE:
        return "";
     case NOTMUCH_CONFIG_AUTOCOMMIT:
index b9be4e2255ad2d03ab82c55b5cad06c7721f2368..61232f1ae3ffcc51bb0dd6b96c91e1b9296e33d3 100644 (file)
@@ -291,6 +291,10 @@ struct _notmuch_database {
 
     /* Track what parameters were specified when opening */
     notmuch_open_param_t params;
+
+    /* list of regular expressions to check for text indexing */
+    regex_t *index_as_text;
+    size_t index_as_text_length;
 };
 
 /* Prior to database version 3, features were implied by the database
index c05d70d34842f1fd1f264cc61a45fbb6348b95ca..737a3f3060677b4e7fd5a55fbe5268082f0867f2 100644 (file)
@@ -1454,9 +1454,11 @@ notmuch_database_remove_message (notmuch_database_t *notmuch,
                                                        &message);
 
     if (status == NOTMUCH_STATUS_SUCCESS && message) {
-       status = _notmuch_message_remove_filename (message, filename);
+       if (notmuch_message_count_files (message) > 1) {
+           status = _notmuch_message_remove_filename (message, filename);
+       }
        if (status == NOTMUCH_STATUS_SUCCESS)
-           _notmuch_message_delete (message);
+           status = _notmuch_message_delete (message);
        else if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
            _notmuch_message_sync (message);
 
@@ -1573,3 +1575,15 @@ notmuch_database_status_string (const notmuch_database_t *notmuch)
 {
     return notmuch->status_string;
 }
+
+bool
+_notmuch_database_indexable_as_text (notmuch_database_t *notmuch, const char *mime_string)
+{
+    for (size_t i = 0; i < notmuch->index_as_text_length; i++) {
+       if (regexec (&notmuch->index_as_text[i], mime_string, 0, NULL, 0) == 0) {
+           return true;
+       }
+    }
+
+    return false;
+}
index 728bfb22ab69c4eef52795cf86d7971c7d42e2fe..629dcb222be8545cfde471ce5184f96c88115881 100644 (file)
@@ -380,6 +380,23 @@ _index_pkcs7_part (notmuch_message_t *message,
                   GMimeObject *part,
                   _notmuch_message_crypto_t *msg_crypto);
 
+static bool
+_indexable_as_text (notmuch_message_t *message, GMimeObject *part)
+{
+    GMimeContentType *content_type = g_mime_object_get_content_type (part);
+    notmuch_database_t *notmuch = notmuch_message_get_database (message);
+
+    if (content_type) {
+       char *mime_string = g_mime_content_type_get_mime_type (content_type);
+       if (mime_string) {
+           bool ret = _notmuch_database_indexable_as_text (notmuch, mime_string);
+           g_free (mime_string);
+           return ret;
+       }
+    }
+    return false;
+}
+
 /* Callback to generate terms for each mime part of a message. */
 static void
 _index_mime_part (notmuch_message_t *message,
@@ -497,9 +514,11 @@ _index_mime_part (notmuch_message_t *message,
        _notmuch_message_add_term (message, "tag", "attachment");
        _notmuch_message_gen_terms (message, "attachment", filename);
 
-       /* XXX: Would be nice to call out to something here to parse
-        * the attachment into text and then index that. */
-       goto DONE;
+       if (! _indexable_as_text (message, part)) {
+           /* XXX: Would be nice to call out to something here to parse
+            * the attachment into text and then index that. */
+           goto DONE;
+       }
     }
 
     byte_array = g_byte_array_new ();
index d5afa30c8e2a3e4d74d50a02efa15659febafbaf..7f5203403723d3514e8d85f738d43b546adcabe7 100644 (file)
 #include "database-private.h"
 #include "message-private.h"
 
+#define LOG_XAPIAN_EXCEPTION(message, error) _log_xapian_exception (__location__, message, error)
+
+static void
+_log_xapian_exception (const char *where, notmuch_message_t *message,  const Xapian::Error error)
+{
+    notmuch_database_t *notmuch = notmuch_message_get_database (message);
+
+    _notmuch_database_log (notmuch,
+                          "A Xapian exception occurred at %s: %s\n",
+                          where,
+                          error.get_msg ().c_str ());
+    notmuch->exception_reported = true;
+}
+
 notmuch_status_t
 notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
 {
@@ -83,10 +97,15 @@ _notmuch_message_modify_property (notmuch_message_t *message, const char *key, c
 
     term = talloc_asprintf (message, "%s=%s", key, value);
 
-    if (delete_it)
-       private_status = _notmuch_message_remove_term (message, "property", term);
-    else
-       private_status = _notmuch_message_add_term (message, "property", term);
+    try {
+       if (delete_it)
+           private_status = _notmuch_message_remove_term (message, "property", term);
+       else
+           private_status = _notmuch_message_add_term (message, "property", term);
+    } catch (Xapian::Error &error) {
+       LOG_XAPIAN_EXCEPTION (message, error);
+       return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+    }
 
     if (private_status)
        return COERCE_STATUS (private_status,
@@ -123,15 +142,22 @@ _notmuch_message_remove_all_properties (notmuch_message_t *message, const char *
     if (status)
        return status;
 
-    _notmuch_message_invalidate_metadata (message, "property");
     if (key)
        term_prefix = talloc_asprintf (message, "%s%s%s", _find_prefix ("property"), key,
                                       prefix ? "" : "=");
     else
        term_prefix = _find_prefix ("property");
 
-    /* XXX better error reporting ? */
-    _notmuch_message_remove_terms (message, term_prefix);
+    try {
+       /* XXX better error reporting ? */
+       _notmuch_message_remove_terms (message, term_prefix);
+    } catch (Xapian::Error &error) {
+       LOG_XAPIAN_EXCEPTION (message, error);
+       return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+    }
+
+    if (! _notmuch_message_frozen (message))
+       _notmuch_message_sync (message);
 
     return NOTMUCH_STATUS_SUCCESS;
 }
index 1c87f8c0c2eb16ecb6b0f372f22e01072ff4fcae..46638f800997d4c5250b3592e4be8ee79abc2a21 100644 (file)
@@ -719,6 +719,8 @@ _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
            /* Ignore failure to remove non-existent term. */
        }
     }
+
+    _notmuch_message_invalidate_metadata (message, "property");
 }
 
 
@@ -941,6 +943,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,
 {
     const char *relative, *directory;
     notmuch_status_t status;
+    notmuch_private_status_t private_status;
     void *local = talloc_new (message);
     char *direntry;
 
@@ -964,10 +967,17 @@ _notmuch_message_add_filename (notmuch_message_t *message,
 
     /* New file-direntry allows navigating to this message with
      * notmuch_directory_get_child_files() . */
-    status = COERCE_STATUS (_notmuch_message_add_term (message, "file-direntry", direntry),
-                           "adding file-direntry term");
-    if (status)
-       return status;
+    private_status = _notmuch_message_add_term (message, "file-direntry", direntry);
+    switch (private_status) {
+    case NOTMUCH_PRIVATE_STATUS_SUCCESS:
+       break;
+    case NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG:
+       _notmuch_database_log (message->notmuch, "filename too long for file-direntry term: %s\n",
+                              filename);
+       return NOTMUCH_STATUS_PATH_ERROR;
+    default:
+       return COERCE_STATUS (private_status, "adding file-direntry term");
+    }
 
     status = _notmuch_message_add_folder_terms (message, directory);
     if (status)
@@ -1383,21 +1393,22 @@ _notmuch_message_delete (notmuch_message_t *message)
     if (status)
        return status;
 
-    message->notmuch->writable_xapian_db->delete_document (message->doc_id);
-
-    /* if this was a ghost to begin with, we are done */
-    private_status = _notmuch_message_has_term (message, "type", "ghost", &is_ghost);
-    if (private_status)
-       return COERCE_STATUS (private_status,
-                             "Error trying to determine whether message was a ghost");
-    if (is_ghost)
-       return NOTMUCH_STATUS_SUCCESS;
-
-    /* look for a non-ghost message in the same thread */
     try {
        Xapian::PostingIterator thread_doc, thread_doc_end;
        Xapian::PostingIterator mail_doc, mail_doc_end;
 
+       /* look for a non-ghost message in the same thread */
+       /* if this was a ghost to begin with, we are done */
+       private_status = _notmuch_message_has_term (message, "type", "ghost", &is_ghost);
+       if (private_status)
+           return COERCE_STATUS (private_status,
+                                 "Error trying to determine whether message was a ghost");
+
+       message->notmuch->writable_xapian_db->delete_document (message->doc_id);
+
+       if (is_ghost)
+           return NOTMUCH_STATUS_SUCCESS;
+
        _notmuch_database_find_doc_ids (message->notmuch, "thread", tid, &thread_doc,
                                        &thread_doc_end);
        _notmuch_database_find_doc_ids (message->notmuch, "type", "mail", &mail_doc, &mail_doc_end);
index 1d3d2b0c9db220d36723728c044f6b0b8e71cfef..367e23e610d0220df108eed1cc078694b10bfea9 100644 (file)
@@ -259,6 +259,10 @@ _notmuch_database_filename_to_direntry (void *ctx,
                                        notmuch_find_flags_t flags,
                                        char **direntry);
 
+bool
+_notmuch_database_indexable_as_text (notmuch_database_t *notmuch,
+                                    const char *mime_string);
+
 /* directory.cc */
 
 notmuch_directory_t *
@@ -722,7 +726,7 @@ notmuch_status_t
 _notmuch_config_load_from_database (notmuch_database_t *db);
 
 notmuch_status_t
-_notmuch_config_load_from_file (notmuch_database_t *db, GKeyFile *file);
+_notmuch_config_load_from_file (notmuch_database_t *db, GKeyFile *file, char **status_string);
 
 notmuch_status_t
 _notmuch_config_load_defaults (notmuch_database_t *db);
index 0b0540b13437f6161adbba741352ae9df0a2cdfd..4e2b0fa48d3daf46dc75f51ded735a95424e9477 100644 (file)
@@ -293,6 +293,8 @@ typedef struct _notmuch_indexopts notmuch_indexopts_t;
  *
  * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory.
  *
+ * NOTMUCH_STATUS_PATH_ERROR: filename is too long
+ *
  * NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to create the
  *     database file (such as permission denied, or file not found,
  *     etc.), or the database already exists.
@@ -720,7 +722,8 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch);
  *
  * The UUID is a NUL-terminated opaque string that uniquely identifies
  * this database.  Two revision numbers are only comparable if they
- * have the same database UUID.
+ * have the same database UUID. The string 'uuid' is owned by notmuch
+ * and should not be freed or modified by the user.
  */
 unsigned long
 notmuch_database_get_revision (notmuch_database_t *notmuch,
@@ -1170,7 +1173,10 @@ notmuch_query_search_threads_st (notmuch_query_t *query, notmuch_threads_t **out
  *
  *     query = notmuch_query_create (database, query_string);
  *
- *     for (messages = notmuch_query_search_messages (query);
+ *     if (notmuch_query_search_messages (query, &messages) != NOTMUCH_STATUS_SUCCESS)
+ *         return EXIT_FAILURE;
+ *
+ *     for (;
  *          notmuch_messages_valid (messages);
  *          notmuch_messages_move_to_next (messages))
  *     {
@@ -2558,6 +2564,7 @@ typedef enum {
     NOTMUCH_CONFIG_USER_NAME,
     NOTMUCH_CONFIG_AUTOCOMMIT,
     NOTMUCH_CONFIG_EXTRA_HEADERS,
+    NOTMUCH_CONFIG_INDEX_AS_TEXT,
     NOTMUCH_CONFIG_LAST
 } notmuch_config_key_t;
 
index 67ff868c2e334eef1895f9ebc801420662508ae3..463e38bf0d8f733fe933da033ab993d828255fb7 100644 (file)
@@ -249,6 +249,11 @@ _choose_database_path (notmuch_database_t *notmuch,
        return NOTMUCH_STATUS_NO_DATABASE;
     }
 
+    if (*message) {
+       free (*message);
+       *message = NULL;
+    }
+
     return NOTMUCH_STATUS_SUCCESS;
 }
 
@@ -320,6 +325,8 @@ _alloc_notmuch (const char *database_path, const char *config_path, const char *
     notmuch->transaction_count = 0;
     notmuch->transaction_threshold = 0;
     notmuch->view = 1;
+    notmuch->index_as_text = NULL;
+    notmuch->index_as_text_length = 0;
 
     notmuch->params = NOTMUCH_PARAM_NONE;
     if (database_path)
@@ -427,6 +434,53 @@ _load_database_state (notmuch_database_t *notmuch)
        notmuch, notmuch->xapian_db->get_uuid ().c_str ());
 }
 
+/* XXX This should really be done lazily, but the error reporting path in the indexing code
+ * would need to be redone to report any errors.
+ */
+notmuch_status_t
+_ensure_index_as_text (notmuch_database_t *notmuch, char **message)
+{
+    int nregex = 0;
+    regex_t *regexv = NULL;
+
+    if (notmuch->index_as_text)
+       return NOTMUCH_STATUS_SUCCESS;
+
+    for (notmuch_config_values_t *list = notmuch_config_get_values (notmuch,
+                                                                   NOTMUCH_CONFIG_INDEX_AS_TEXT);
+        notmuch_config_values_valid (list);
+        notmuch_config_values_move_to_next (list)) {
+       regex_t *new_regex;
+       int rerr;
+       const char *str = notmuch_config_values_get (list);
+       size_t len = strlen (str);
+
+       /* str must be non-empty, because n_c_get_values skips empty
+        * strings */
+       assert (len > 0);
+
+       regexv = talloc_realloc (notmuch, regexv, regex_t, nregex + 1);
+       new_regex = &regexv[nregex];
+
+       rerr = regcomp (new_regex, str, REG_EXTENDED | REG_NOSUB);
+       if (rerr) {
+           size_t error_size = regerror (rerr, new_regex, NULL, 0);
+           char *error = (char *) talloc_size (str, error_size);
+
+           regerror (rerr, new_regex, error, error_size);
+           IGNORE_RESULT (asprintf (message, "Error in index.as_text: %s: %s\n", error, str));
+
+           return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
+       }
+       nregex++;
+    }
+
+    notmuch->index_as_text = regexv;
+    notmuch->index_as_text_length = nregex;
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
 static notmuch_status_t
 _finish_open (notmuch_database_t *notmuch,
              const char *profile,
@@ -506,7 +560,7 @@ _finish_open (notmuch_database_t *notmuch,
            goto DONE;
 
        if (key_file)
-           status = _notmuch_config_load_from_file (notmuch, key_file);
+           status = _notmuch_config_load_from_file (notmuch, key_file, &message);
        if (status)
            goto DONE;
 
@@ -531,6 +585,10 @@ _finish_open (notmuch_database_t *notmuch,
        if (status)
            goto DONE;
 
+       status = _ensure_index_as_text (notmuch, &message);
+       if (status)
+           goto DONE;
+
        autocommit_str = notmuch_config_get (notmuch, NOTMUCH_CONFIG_AUTOCOMMIT);
        if (unlikely (! autocommit_str)) {
            INTERNAL_ERROR ("missing configuration for autocommit");
@@ -908,7 +966,7 @@ notmuch_database_load_config (const char *database_path,
     }
 
     if (key_file) {
-       status = _notmuch_config_load_from_file (notmuch, key_file);
+       status = _notmuch_config_load_from_file (notmuch, key_file, &message);
        if (status)
            goto DONE;
     }
index 707f6222b16bd001c17097439e49804a6babc4ef..1c60c122c8e9eedd22b2e5481e8e189719551994 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "notmuch-private.h"
 #include "database-private.h"
+#include "xapian-extra.h"
 
 #include <glib.h> /* GHashTable, GPtrArray */
 
@@ -186,7 +187,7 @@ _notmuch_query_string_to_xapian_query (notmuch_database_t *notmuch,
 {
     try {
        if (query_string == "" || query_string == "*") {
-           output = Xapian::Query::MatchAll;
+           output = xapian_query_match_all ();
        } else {
            output =
                notmuch->query_parser->
index 539915d892f9529d3d011dfa7611bb37aba718d2..3a775261fc3a1c2f31b82f345766e231fbf45f81 100644 (file)
@@ -25,6 +25,7 @@
 #include "regexp-fields.h"
 #include "notmuch-private.h"
 #include "database-private.h"
+#include "xapian-extra.h"
 
 notmuch_status_t
 compile_regex (regex_t &regexp, const char *str, std::string &msg)
@@ -200,7 +201,7 @@ RegexpFieldProcessor::operator() (const std::string & str)
     if (str.empty ()) {
        if (options & NOTMUCH_FIELD_PROBABILISTIC) {
            return Xapian::Query (Xapian::Query::OP_AND_NOT,
-                                 Xapian::Query::MatchAll,
+                                 xapian_query_match_all (),
                                  Xapian::Query (Xapian::Query::OP_WILDCARD, term_prefix));
        } else {
            return Xapian::Query (term_prefix);
index e3a81b4fe1457a030154fb3cb05cfc59cd3bb059..99bc2ea2450fe7a188f527106d5f45fb711f8f10 100644 (file)
@@ -86,10 +86,14 @@ _notmuch_string_map_append (notmuch_string_map_t *map,
 static int
 cmppair (const void *pa, const void *pb)
 {
+    int cmp = 0;
     notmuch_string_pair_t *a = (notmuch_string_pair_t *) pa;
     notmuch_string_pair_t *b = (notmuch_string_pair_t *) pb;
 
-    return strcmp (a->key, b->key);
+    cmp = strcmp (a->key, b->key);
+    if (cmp == 0)
+       cmp = strcmp (a->value, b->value);
+    return cmp;
 }
 
 static void
index 21b49908ae246c1bffd0b7cc242b79b663cbb800..1a87240d3c21de8e51736ede9b408cdd36e23652 100644 (file)
@@ -77,6 +77,8 @@ typedef struct notmuch_show_params {
     bool output_body;
     int duplicate;
     int part;
+    int offset;
+    int limit;
     _notmuch_crypto_t crypto;
     bool include_html;
     GMimeStream *out_stream;
index e9456d794456c080e81abbd6a00efcb6b123c3c4..8123e4381003bd89509edc8d7970737893b1992b 100644 (file)
@@ -278,18 +278,24 @@ notmuch_conffile_open (notmuch_database_t *notmuch,
        return NULL;
     }
 
-    if (config->is_new)
-       g_key_file_set_comment (config->key_file, NULL, NULL,
-                               toplevel_config_comment, NULL);
-
     for (size_t i = 0; i < ARRAY_SIZE (group_comment_table); i++) {
        const char *name = group_comment_table[i].group_name;
        if (! g_key_file_has_group (config->key_file,  name)) {
            /* Force group to exist before adding comment */
            g_key_file_set_value (config->key_file, name, "dummy_key", "dummy_val");
            g_key_file_remove_key (config->key_file, name, "dummy_key", NULL);
-           g_key_file_set_comment (config->key_file, name, NULL,
-                                   group_comment_table[i].comment, NULL);
+           if (config->is_new && (i == 0) ) {
+               const char *comment;
+
+               comment = talloc_asprintf (config, "%s\n%s",
+                                          toplevel_config_comment,
+                                          group_comment_table[i].comment);
+               g_key_file_set_comment (config->key_file, name, NULL, comment,
+                                       NULL);
+           } else {
+               g_key_file_set_comment (config->key_file, name, NULL,
+                                       group_comment_table[i].comment, NULL);
+           }
        }
     }
     return config;
index ceb86fbc14dfa97db10dfec87732452a648edc0b..97073c804a724e4819598089a410c336e9ea48d5 100644 (file)
@@ -31,7 +31,6 @@ import locale as _locale
 import logging as _logging
 import os as _os
 import re as _re
-import shutil as _shutil
 import subprocess as _subprocess
 import sys as _sys
 import tempfile as _tempfile
@@ -248,13 +247,13 @@ def count_messages(prefix=None):
         stdout=_subprocess.PIPE, wait=True)
     if status != 0:
         _LOG.error("failed to run notmuch config")
-        sys.exit(1)
+        _sys.exit(1)
     return int(stdout.rstrip())
 
 def get_tags(prefix=None):
     "Get a list of tags with a given prefix."
     (status, stdout, stderr) = _spawn(
-        args=['notmuch', 'search', '--query=sexp', '--output=tags', _tag_query(prefix)],
+        args=['notmuch', 'search', '--exclude=false', '--query=sexp', '--output=tags', _tag_query(prefix)],
         stdout=_subprocess.PIPE, wait=True)
     return [tag for tag in stdout.splitlines()]
 
@@ -377,7 +376,7 @@ def check_safe_fraction(status):
 
     total = count_messages (TAG_PREFIX)
     if total == 0:
-        _LOG.error('No existing tags with given prefix, stopping.'.format(safe))
+        _LOG.error('No existing tags with given prefix, stopping.')
         _LOG.error('Use --force to override.')
         exit(1)
     change = len(status['added'])+len(status['deleted'])
@@ -719,7 +718,7 @@ class DatabaseCache:
                 self._known[id] = False
         else:
             (_, stdout, stderr) = _spawn(
-                args=['notmuch', 'search', '--output=files', 'id:{0}'.format(id)],
+                args=['notmuch', 'search', '--exclude=false', '--output=files', 'id:{0}'.format(id)],
                 stdout=_subprocess.PIPE,
                 wait=True)
             self._known[id] = stdout != None
index 346e64697aaf02763a38eb09e93ef428348aae4a..4a53e3ebcc064cc531b8cb6255c422ff35938b30 100644 (file)
@@ -413,6 +413,10 @@ add_file (notmuch_database_t *notmuch, const char *filename,
     case NOTMUCH_STATUS_FILE_NOT_EMAIL:
        fprintf (stderr, "Note: Ignoring non-mail file: %s\n", filename);
        break;
+    case NOTMUCH_STATUS_PATH_ERROR:
+       fprintf (stderr, "Note: Ignoring non-indexable path: %s\n", filename);
+       (void) print_status_database ("add_file", notmuch, status);
+       break;
     case NOTMUCH_STATUS_FILE_ERROR:
        /* Someone renamed/removed the file between scandir and now. */
        state->vanished_files++;
index ee9efa7448d78c171ec618d561bb1fa450576864..7fb40ce9ab5d3f0179707a7c0ea88f9440673f25 100644 (file)
@@ -1159,6 +1159,18 @@ do_show_threaded (void *ctx,
     notmuch_thread_t *thread;
     notmuch_messages_t *messages;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
+    int i;
+
+    if (params->offset < 0) {
+       unsigned count;
+       notmuch_status_t s = notmuch_query_count_threads (query, &count);
+       if (print_status_query ("notmuch show", query, s))
+           return 1;
+
+       params->offset += count;
+       if (params->offset < 0)
+           params->offset = 0;
+    }
 
     status = notmuch_query_search_threads (query, &threads);
     if (print_status_query ("notmuch show", query, status))
@@ -1166,11 +1178,16 @@ do_show_threaded (void *ctx,
 
     sp->begin_list (sp);
 
-    for (;
-        notmuch_threads_valid (threads);
-        notmuch_threads_move_to_next (threads)) {
+    for (i = 0;
+        notmuch_threads_valid (threads) && (params->limit < 0 || i < params->offset + params->limit);
+        notmuch_threads_move_to_next (threads), i++) {
        thread = notmuch_threads_get (threads);
 
+       if (i < params->offset) {
+           notmuch_thread_destroy (thread);
+           continue;
+       }
+
        messages = notmuch_thread_get_toplevel_messages (thread);
 
        if (messages == NULL)
@@ -1201,6 +1218,18 @@ do_show_unthreaded (void *ctx,
     notmuch_message_t *message;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
     notmuch_bool_t excluded;
+    int i;
+
+    if (params->offset < 0) {
+       unsigned count;
+       notmuch_status_t s = notmuch_query_count_messages (query, &count);
+       if (print_status_query ("notmuch show", query, s))
+           return 1;
+
+       params->offset += count;
+       if (params->offset < 0)
+           params->offset = 0;
+    }
 
     status = notmuch_query_search_messages (query, &messages);
     if (print_status_query ("notmuch show", query, status))
@@ -1208,9 +1237,13 @@ do_show_unthreaded (void *ctx,
 
     sp->begin_list (sp);
 
-    for (;
-        notmuch_messages_valid (messages);
-        notmuch_messages_move_to_next (messages)) {
+    for (i = 0;
+        notmuch_messages_valid (messages) && (params->limit < 0 || i < params->offset + params->limit);
+        notmuch_messages_move_to_next (messages), i++) {
+       if (i < params->offset) {
+           continue;
+       }
+
        sp->begin_list (sp);
        sp->begin_list (sp);
 
@@ -1287,6 +1320,8 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
     notmuch_show_params_t params = {
        .part = -1,
        .duplicate = 0,
+       .offset = 0,
+       .limit = -1, /* unlimited */
        .omit_excluded = true,
        .output_body = true,
        .crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
@@ -1328,6 +1363,8 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
        { .opt_bool = &params.output_body, .name = "body" },
        { .opt_bool = &params.include_html, .name = "include-html" },
        { .opt_int = &params.duplicate, .name = "duplicate" },
+       { .opt_int = &params.limit, .name = "limit" },
+       { .opt_int = &params.offset, .name = "offset" },
        { .opt_inherit = notmuch_shared_options },
        { }
     };
index 37286b8f491af6428c0d22c3222a01f85039a369..814b9e42a59f46ae675f24f688005ec22ab58914 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -563,6 +563,12 @@ main (int argc, char *argv[])
                                               NULL,
                                               &notmuch,
                                               &status_string);
+       if (status_string) {
+           fputs (status_string, stderr);
+           free (status_string);
+           status_string = NULL;
+       }
+
        switch (status) {
        case NOTMUCH_STATUS_NO_CONFIG:
            if (! (command->mode & NOTMUCH_COMMAND_CONFIG_CREATE)) {
@@ -584,6 +590,8 @@ main (int argc, char *argv[])
        case NOTMUCH_STATUS_SUCCESS:
            break;
        default:
+           fputs ("Error: unable to load config file.\n", stderr);
+           ret = 1;
            goto DONE;
        }
 
index 59b37b1b8ff033092591ede74f9b1f7eb551ec62..1ca0df2f664c9e711a6ec0977ff76b8fce60036e 100644 (file)
@@ -24,11 +24,11 @@ Getting set up to run tests:
 First, you need to get the corpus.  If you don't already have the gpg
 key for David Bremner, run
 
-   % gpg --search 'david@tethera.net'
+   % gpg --locate-external-key 'david@tethera.net'
 
 This should get you a key with fingerprint
 
-    815B 6398 2A79 F8E7 C727  86C4 762B 57BB 7842 06AD
+    7A18 807F 100A 4570 C596  8420 7E4E 65C8 720B 706B
 
 (the last 8 digits are printed as the "key id").
 
index 9c895d6a910145266dde528039a61caabcc8fa70..47fdb0c27ab54df243403ba5a1cfe3595d256c8b 100755 (executable)
@@ -11,4 +11,13 @@ time_run 'tag * +existing_tag' "notmuch tag +new_tag '*'"
 time_run 'tag * -existing_tag' "notmuch tag -new_tag '*'"
 time_run 'tag * -missing_tag' "notmuch tag -new_tag '*'"
 
+time_run 'tag * +maildir_flag F' "notmuch tag +flagged '*'"
+time_run 'tag * -maildir_flag F' "notmuch tag -flagged '*'"
+time_run 'tag * +maildir_flag P' "notmuch tag +passed '*'"
+time_run 'tag * -maildir_flag P' "notmuch tag -passed '*'"
+time_run 'tag * +maildir_flag D' "notmuch tag +draft '*'"
+time_run 'tag * -maildir_flag D' "notmuch tag -draft '*'"
+time_run 'tag * +maildir_flag S' "notmuch tag -unread '*'"
+time_run 'tag * -maildir_flag S' "notmuch tag +unread '*'"
+
 time_done
diff --git a/performance-test/download/notmuch-email-corpus-0.5.tar.xz.asc b/performance-test/download/notmuch-email-corpus-0.5.tar.xz.asc
new file mode 100644 (file)
index 0000000..2318c2f
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCAAdFiEEkiyHYXwaY0SiY6fqA0U5G1WqFSEFAmR119gACgkQA0U5G1Wq
+FSHlSQ/+NSRj27PEZjaP2I+3j+rsMG3pnVckNcuOQyfgjJ+zEagMZyRu3vaIA/pX
+xtBrNIX4l4CQIkqwyNjsuqJdzh6S3DeCWSEr1Q+GSBki+wQCBiRuDYY2HQoDezEK
+4bMfniEWZpKJD8PfIabz0OOqMUsfXEYMd9kefew5/J4OGnDIv8E5pKfqvDNNO4rW
+MhZ9w9uR9wkvmfmpO66kAgTfLllwiyNHWoWnzQfNmqM8eULFn7XxM1PEZShUEqXf
+pTWCqqm5OyUcy8f+gy9Mb7DRRvnwLpHHRQlCzzH2c+ENQRpt1ErsgVKpHTVk4UsB
+EML+zwyWEaQg7xVKWXRJDuGCF47S1GCQNUtvtx57HJl6Ds6N2mlr2KEGaI7qtiz5
+5hdaTc0L/TVN0WS+uCdfdDDozFErf1kwhA6Jnpi0YNNdK+wpFzj7ISvA+DNHwJ75
+TLBuJIU/h3QfX3NDC5xDbsWAgpv7a84e7ePO6+kAVkHsNYDbFjiunr5fRbqDsJcJ
+B+aVGhKvFZbziC84Dn5Ae9Lpa40fQlxbdb+So2nDIiuR3P33vt7wr/e2ptVfrqkn
+a1DM96n03VWexwEDMye3b3rOTXsN5Ul87zucg9xWm5JT75NGuqJ1WDJN/wwNPDro
+ZXS1OHh7UKsU1tP2J9+gLiKYNBP4m4BQjEgYXpiYEoge9A1QplQ=
+=5/Ep
+-----END PGP SIGNATURE-----
index f02527a7061f05c5135263e6ff483efafd5f2af4..357b9dad2e8c2828ca75bcca4845115b453b612b 100644 (file)
@@ -1,3 +1,3 @@
 # this should be both a valid Makefile fragment and valid POSIX(ish) shell.
 
-PERFTEST_VERSION=0.4
+PERFTEST_VERSION=0.5
index 10f127cbbef171be491b3bc8f5b742b8e8c67111..a81808b164f0cec0ec6c0010cb023fe77d7e098c 100644 (file)
@@ -137,6 +137,23 @@ detection of missing prerequisites. In the future we may treat tests
 unable to run because of missing prerequisites, but not explicitly
 skipped by the user, as failures.
 
+Testing installed notmuch
+-------------------------
+
+Systems integrators (e.g. Linux distros) may wish to test an installed
+version of notmuch.  This can be done be running
+
+     $ NOTMUCH_TEST_INSTALLED=1 ./test/notmuch-test
+
+In this scenario the test suite does not assume a built tree, and in
+particular cannot rely on the output of 'configure'. You may want to
+set certain feature environment variables ('NOTMUCH_HAVE_*') directly
+if you know those apply to your installed notmuch). Consider also
+setting TERM=dumb if the value of TERM cannot be used (e.g. in a
+chroot with missing terminfo). Note that having a built tree may cause
+surprising/broken results for NOTMUCH_TEST_INSTALLED, so consider
+cleaning first.
+
 Writing Tests
 -------------
 The test script is written as a shell script. It is to be named as
index a2f4d93fd2652a832aad55466c602a51963dfc2f..642f918d20398c4b9b8f028183cfd10e130da5e8 100755 (executable)
@@ -66,6 +66,7 @@ test_begin_subtest 'NOTMUCH_CONFIG is set and points to an existing file'
 test_expect_success 'test -f "${NOTMUCH_CONFIG}"'
 
 test_begin_subtest 'PATH is set to build directory'
+test_subtest_broken_for_installed
 test_expect_equal \
     "$(dirname ${TEST_DIRECTORY})" \
     "$(echo $PATH|cut -f1 -d: | sed -e 's,/test/valgrind/bin$,,')"
index da45d3aecf7ae7a0c6e86be06f6d21155c50129b..827edc149a727bb5427fcb53d8a11ab6516b9bc1 100755 (executable)
@@ -12,13 +12,16 @@ test_expect_success 'notmuch help'
 test_begin_subtest 'notmuch --version'
 test_expect_success 'notmuch --version'
 
-if [ $NOTMUCH_HAVE_MAN -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_MAN-0}" = "1" ]; then
     test_begin_subtest 'notmuch --help tag'
     test_expect_success 'notmuch --help tag'
 
     test_begin_subtest 'notmuch help tag'
     test_expect_success 'notmuch help tag'
 else
+    if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       test_done
+    fi
     test_begin_subtest 'notmuch --help tag (man pages not available)'
     test_expect_success 'test_must_fail notmuch --help tag >/dev/null'
 
index 43bbce31057d4fc108621c505057618d5b0d9990..621e0b69721091a5f7383db7db726004fadc2c4f 100755 (executable)
@@ -3,6 +3,8 @@
 test_description='"notmuch config"'
 . $(dirname "$0")/test-lib.sh || exit 1
 
+cp notmuch-config initial-config
+
 test_begin_subtest "Get string value"
 test_expect_equal "$(notmuch config get user.name)" "Notmuch Test Suite"
 
@@ -57,6 +59,7 @@ database.mail_root=MAIL_DIR
 database.path=MAIL_DIR
 foo.list=this;is another;list value;
 foo.string=this is another string value
+index.as_text=
 maildir.synchronize_flags=true
 new.ignore=
 new.tags=unread;inbox
@@ -192,4 +195,17 @@ test_begin_subtest "get built_with.nonexistent prints false"
 output=$(notmuch config get built_with.nonexistent)
 test_expect_equal "$output" "false"
 
+test_begin_subtest "Bad utf8 reported as error"
+cp initial-config bad-config
+printf '[query]\nq3=from:\xff\n' >>bad-config
+test_expect_code 1 "notmuch --config=./bad-config config list"
+
+test_begin_subtest "Specific error message about bad utf8"
+notmuch --config=./bad-config config list 2>ERRORS
+cat <<EOF > EXPECTED
+GLib: Key file contains key “q3” with value “from:�” which is not UTF-8
+Error: unable to load config file.
+EOF
+test_expect_equal_file EXPECTED ERRORS
+
 test_done
index 10b29ec33f3a726f261feb5df98d58789643cf3e..39846d34b7f32f3912619bf49d30d90c4ab67f4d 100755 (executable)
@@ -21,7 +21,8 @@ baz
 EOF
 
 expected_dir=$NOTMUCH_SRCDIR/test/setup.expected-output
-test_expect_equal_file ${expected_dir}/config-with-comments new-notmuch-config
+sed '/^$/d' < new-notmuch-config > filtered-config
+test_expect_equal_file ${expected_dir}/config-with-comments filtered-config
 
 test_begin_subtest "setup consistent with config-set for single items"
 # note this relies on the config state from the previous test.
index cb67889c95f82d6a4d130d65560fb453ff8d5efe..52888be22a86acdf15a83a532c5d80316b738aae 100755 (executable)
@@ -383,7 +383,22 @@ No new mail. Removed 1 message.
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "Long file names have reasonable diagnostics"
+printf -v name 'f%.0s' {1..234}
+generate_message "[filename]=$name"
+notmuch new 2>&1 | notmuch_dir_sanitize >OUTPUT
+rm ${MAIL_DIR}/${name}
+cat <<EOF > EXPECTED
+Note: Ignoring non-indexable path: MAIL_DIR/$name
+add_file: Path supplied is illegal for this function
+filename too long for file-direntry term: MAIL_DIR/$name
+Processed 1 file in almost no time.
+No new mail.
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "Xapian exception: read only files"
+test_subtest_broken_for_root
 chmod u-w ${MAIL_DIR}/.notmuch/xapian/*.*
 output=$(NOTMUCH_NEW --debug 2>&1 | sed 's/: .*$//' )
 chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.*
@@ -455,12 +470,4 @@ Date: Fri, 17 Jun 2016 22:14:41 -0400
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
-add_email_corpus indexing
-
-test_begin_subtest "index text/* attachments"
-test_subtest_known_broken
-notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain > EXPECTED
-notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz > OUTPUT
-test_expect_equal_file_nonempty EXPECTED OUTPUT
-
 test_done
index fe2953247302db5d1a5da8568b5667c1a64a000c..1feb5624c41b8af06fab0750f7bc70395b7a4504 100755 (executable)
@@ -299,6 +299,7 @@ database.backup_dir
 database.hook_dir
 database.mail_root=MAIL_DIR
 database.path
+index.as_text=
 maildir.synchronize_flags=true
 new.ignore=
 new.tags=unread;inbox
@@ -373,6 +374,11 @@ EOF
           notmuch new
           test_expect_equal "$(xapian-metadata get ${XAPIAN_PATH} version)" 3
           ;;
+       home_mail|maildir_env)
+          test_begin_subtest "No errors from config list ($config)"
+          notmuch config list 2>OUTPUT 1>/dev/null
+          test_expect_equal_file /dev/null OUTPUT
+          ;;
        *)
           backup_database
           test_begin_subtest ".notmuch without xapian/ handled gracefully ($config)"
index 4814670681165322d4769487991ef4f12542ea64..6e855b591af5594677c73d1d0a7271e0eda05ece 100755 (executable)
@@ -157,7 +157,7 @@ print("4: {} messages".format(query.count_messages()))
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
 
     test_begin_subtest "and of exact terms (query=sexp)"
     output=$(notmuch count --query=sexp '(and "wonderful" "wizard")')
index a3f0dead1420239fd654c89ea04531d1ee141729..515eb88f34b3f9c69bca5b13cd6c5cc9c1b8c44d 100755 (executable)
@@ -34,6 +34,12 @@ add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"
 output=$(notmuch search id:${gen_msg_id} | notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)"
 
+test_begin_subtest "Message-Id with spaces"
+test_subtest_known_broken
+add_message '[id]="message id@example.com"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search --output=messages id:"message id@example.com")
+test_expect_equal "$output" "messageid@example.com"
+
 test_begin_subtest "Search by mid:"
 add_message '[subject]="search by mid"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
 output=$(notmuch search mid:${gen_msg_id} | notmuch_search_sanitize)
@@ -132,6 +138,7 @@ thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; Message-Id with spaces (inbox unread)
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by mid (inbox unread)
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)
index 0c7db9c248bdfe2e143dab1f9f63d9ceaded9256..8800b545982751c1ac5dc0716fa605f4c72fe77c 100755 (executable)
@@ -2,7 +2,7 @@
 test_description='"notmuch search" in several variations'
 . $(dirname "$0")/test-lib.sh || exit 1
 
-if [ $NOTMUCH_HAVE_SFSEXP -ne 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" != "1" ]; then
     printf "Skipping due to missing sfsexp library\n"
     test_done
 fi
index 8bb3627ac1f0e39307d134548b4a2122ddcba51d..0cafbe20f11a0140cac8df350d6e9900ec52251e 100755 (executable)
@@ -325,7 +325,7 @@ cat <<EOF >EXPECTED
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
-if [[ NOTMUCH_HAVE_SFSEXP = 1 ]]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
     test_begin_subtest "sexpr query: all messages"
     notmuch address '*' > EXPECTED
     notmuch address --query=sexp '()' > OUTPUT
diff --git a/test/T131-show-limiting.sh b/test/T131-show-limiting.sh
new file mode 100755 (executable)
index 0000000..30d1f25
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+test_description='"notmuch show" --offset and --limit parameters'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus
+
+show () {
+    local kind="$1"
+    shift
+    if [ "$kind" = messages ]; then
+        set -- --unthreaded "$@"
+    fi
+    notmuch show --body=false --format=text --entire-thread=false "$@" "*" |
+        sed -nre 's/^.message\{.*\<depth:0\>.*/&/p'
+}
+
+for outp in messages threads; do
+    test_begin_subtest "$outp: limit does the right thing"
+    show $outp | head -n 20 >expected
+    show $outp --limit=20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: concatenation of limited shows"
+    show $outp | head -n 20 >expected
+    show $outp --limit=10 >output
+    show $outp --limit=10 --offset=10 >>output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: limit larger than result set"
+    N=$(notmuch count --output=$outp "*")
+    show $outp >expected
+    show $outp --limit=$((1 + N)) >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: limit = 0"
+    test_expect_equal "$(show $outp --limit=0)" ""
+
+    test_begin_subtest "$outp: offset does the right thing"
+    # note: tail -n +N is 1-based
+    show $outp | tail -n +21 >expected
+    show $outp --offset=20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: offset = 0"
+    show $outp >expected
+    show $outp --offset=0 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: negative offset"
+    show $outp | tail -n 20 >expected
+    show $outp --offset=-20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: negative offset"
+    show $outp | tail -n 1 >expected
+    show $outp --offset=-1 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: negative offset combined with limit"
+    show $outp | tail -n 20 | head -n 10 >expected
+    show $outp --offset=-20 --limit=10 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: negative offset combined with equal limit"
+    show $outp | tail -n 20 >expected
+    show $outp --offset=-20 --limit=20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: negative offset combined with large limit"
+    show $outp | tail -n 20 >expected
+    show $outp --offset=-20 --limit=50 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: negative offset larger than results"
+    N=$(notmuch count --output=$outp "*")
+    show $outp >expected
+    show $outp --offset=-$((1 + N)) >output
+    test_expect_equal_file expected output
+done
+
+test_done
index 1a2fd77e4853708580293c9695a61cab0398fe8e..273c0af35927e092c37b7a9c47cacfc9b8aaac89 100755 (executable)
@@ -320,6 +320,7 @@ test_begin_subtest "Tag name beginning with -"
 test_expect_code 1 'notmuch tag +- One'
 
 test_begin_subtest "Xapian exception: read only files"
+test_subtest_broken_for_root
 chmod u-w ${MAIL_DIR}/.notmuch/xapian/*.*
 output=$(notmuch tag +something '*' 2>&1 | sed 's/: .*$//' )
 chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.*
@@ -327,7 +328,7 @@ test_expect_equal "$output" "A Xapian exception occurred opening database"
 
 add_email_corpus
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
 
     test_query_syntax '(and "wonderful" "wizard")' 'wonderful and wizard'
     test_query_syntax '(or "php" "wizard")' 'php or wizard'
index 4a797f6aad14dfe13c18c8a69d3eac617dcee80e..318c97880150d12dcb36f312c5794a04e3898af8 100755 (executable)
@@ -65,8 +65,9 @@ test_expect_equal_json "$output" "[{\"thread\": \"XXX\",
  \"tags\": [\"inbox\",
  \"unread\"]}]"
 
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" ]; then
 test_begin_subtest "Search message: json, 64-bit timestamp"
-if [ $NOTMUCH_HAVE_64BIT_TIME_T -ne 1 ]; then
+if [ "${NOTMUCH_HAVE_64BIT_TIME_T-0}" != "1" ]; then
     test_subtest_known_broken
 fi
 add_message "[subject]=\"json-search-64bit-timestamp-subject\"" "[date]=\"Tue, 01 Jan 2999 12:00:00 -0000\"" "[body]=\"json-search-64bit-timestamp-message\""
@@ -81,6 +82,7 @@ test_expect_equal_json "$output" "[{\"thread\": \"XXX\",
  \"query\": [\"id:$gen_msg_id\", null],
  \"tags\": [\"inbox\",
  \"unread\"]}]"
+fi # NOTMUCH_TEST_INSTALLED undefined / empty
 
 test_begin_subtest "Format version: too low"
 test_expect_code 20 "notmuch search --format-version=0 \\*"
index 207f578810133643491dad884fafb879a8e62312..120d7138d61809fd46cb724501bce921de13db87 100755 (executable)
@@ -24,7 +24,7 @@ test_begin_subtest "Basic reply"
 notmuch reply id:${gen_msg_id} >OUTPUT 2>&1 && echo OK >> OUTPUT
 test_expect_equal_file basic.expected OUTPUT
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
 
     test_begin_subtest "Basic reply (query=sexp)"
     notmuch reply --query=sexp "(id ${gen_msg_id})" >OUTPUT 2>&1 && echo OK >> OUTPUT
index a86f0fb7e8f0440de6268075e91ba3b4262a38b1..c3f188398b87e6943d6e3a568191ed8303431605 100755 (executable)
@@ -118,7 +118,7 @@ notmuch dump -- from:cworth > dump-dash-cworth.actual
 test_expect_equal_file dump-cworth.expected dump-dash-cworth.actual
 
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
 
     test_begin_subtest "dump --query=sexp -- '(from cworth)'"
     notmuch dump --query=sexp -- '(from cworth)' > dump-dash-cworth.actual2
@@ -139,6 +139,7 @@ notmuch dump --output=dump-outfile-dash-inbox.actual -- from:cworth
 test_expect_equal_file dump-cworth.expected dump-outfile-dash-inbox.actual
 
 test_begin_subtest "Check for a safe set of message-ids"
+test_subtest_broken_for_installed
 notmuch search --output=messages from:cworth | sed s/^id:// > EXPECTED
 notmuch search --output=messages from:cworth | sed s/^id:// |\
        $TEST_DIRECTORY/hex-xcode --direction=encode > OUTPUT
@@ -246,6 +247,7 @@ notmuch dump --format=batch-tag > OUTPUT.$test_count
 test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
 
 test_begin_subtest 'format=batch-tag, checking encoded output'
+test_subtest_broken_for_installed
 NOTMUCH_DUMP_TAGS --format=batch-tag -- from:cworth |\
         awk "{ print \"+$enc1 +$enc2 +$enc3 -- \" \$5 }" > EXPECTED.$test_count
 NOTMUCH_DUMP_TAGS --format=batch-tag -- from:cworth > OUTPUT.$test_count
index 9d0df187da73af4c4319a621442e6342f728bad2..e96c16013443f57eab16ae55581a73d4256e9dee 100755 (executable)
@@ -258,6 +258,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "Verify that sent messages are saved/searchable (via FCC)"
+test_subtest_broken_for_installed
 notmuch new > /dev/null
 output=$(notmuch search 'subject:"testing message sent via SMTP"' | notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; Testing message sent via SMTP (inbox)"
@@ -350,6 +351,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "Reply within emacs"
+test_subtest_broken_for_installed
 test_emacs '(let ((message-hidden-headers ''()))
            (notmuch-search "subject:\"testing message sent via SMTP\"")
            (notmuch-test-wait)
@@ -1030,7 +1032,7 @@ End of search results.
 YYY/notmuch_fail exited with status 1 (see *Notmuch errors* for more details)
 === ERROR ===
 YYY/notmuch_fail exited with status 1
-command: YYY/notmuch_fail search --format\=sexp --format-version\=5 --sort\=newest-first tag\:inbox
+command: YYY/notmuch_fail search --format\=sexp --format-version\=5 --sort\=newest-first --exclude\=false tag\:inbox
 exit status: 1"
 
 test_begin_subtest "Search handles subprocess warnings"
index 721cbfdd60f24f425a69c2211336d3bc6fc0bd44..27c0e86d42b672b6442c8ab6981a468a48bc34e0 100755 (executable)
@@ -30,6 +30,7 @@ msg_file=$(notmuch search --output=files subject:signed-message-sent-via-SMTP)
 test_expect_equal_message_body sent_message "$msg_file"
 
 test_begin_subtest "signed part content-type indexing"
+test_subtest_broken_for_installed
 notmuch search mimetype:multipart/signed and mimetype:application/pgp-signature | notmuch_search_sanitize > OUTPUT
 cat <<EOF >EXPECTED
 thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; test signed message 001 (inbox signed)
index 809274ce14a2156ab56d68c0d0e524da3baecab6..d2118c0492efb9e3bd9bedf639b9a4f859a30ea5 100755 (executable)
@@ -183,8 +183,9 @@ test_begin_subtest "show PKCS#7 SignedData outputs valid JSON"
 output=$(notmuch show --format=json id:smime-onepart-signed@protected-headers.example)
 test_valid_json "$output"
 
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" ]; then
 test_begin_subtest "Verify signature on PKCS#7 SignedData message"
-if [ $NOTMUCH_HAVE_64BIT_TIME_T -ne 1 ]; then
+if [ "${NOTMUCH_HAVE_64BIT_TIME_T-0}" != "1" ]; then
     test_subtest_known_broken
 fi
 output=$(notmuch show --format=json id:smime-onepart-signed@protected-headers.example)
@@ -194,6 +195,7 @@ test_json_nodes <<<"$output" \
                 'expires:[0][0][0]["crypto"]["signed"]["status"][0]["expires"]=2611032858' \
                 'fingerprint:[0][0][0]["crypto"]["signed"]["status"][0]["fingerprint"]="702BA4B157F1E2B7D16B0C6A5FFC8A7DE2057DEB"' \
                 'status:[0][0][0]["crypto"]["signed"]["status"][0]["status"]="good"'
+fi # NOTMUCH_TEST_INSTALLED undefined / empty
 
 test_begin_subtest "Verify signature on PKCS#7 SignedData message signer User ID"
 if [ $NOTMUCH_GMIME_X509_CERT_VALIDITY -ne 1 ]; then
index 642457bf2c1f3220e7840631c76174e4a9cf9790..ff06ff691b785c96bbe5762a247868d663685864 100755 (executable)
@@ -11,6 +11,10 @@ test_description='exception symbol hiding'
 
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 test_begin_subtest 'running test' run_test
 mkdir -p ${PWD}/fakedb/.notmuch
 $TEST_DIRECTORY/symbol-test ${PWD}/fakedb ${PWD}/nonexistent 2>&1 \
index 9f71ce3c414916a65bb20f18273d2635378a8ecb..21912431d3af15f6cc9cf83b728d918e17e3208c 100755 (executable)
@@ -4,6 +4,10 @@ test_description="python bindings"
 
 test_require_external_prereq ${NOTMUCH_PYTHON}
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 add_email_corpus
 add_gnupg_home
 
index 30872af0b9816a4e00da45a5f63b74844bcc84b2..0059b050c718a1452ae994e32d605d370e18c65b 100755 (executable)
@@ -2,10 +2,13 @@
 test_description="python bindings (pytest)"
 . $(dirname "$0")/test-lib.sh || exit 1
 
-if [ $NOTMUCH_HAVE_PYTHON3_CFFI -eq 0 -o $NOTMUCH_HAVE_PYTHON3_PYTEST -eq 0 ]; then
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
     test_done
 fi
 
+if [ "${NOTMUCH_HAVE_PYTHON3_CFFI-0}" = "0" -o "${NOTMUCH_HAVE_PYTHON3_PYTEST-0}" = "0" ]; then
+    test_done
+fi
 
 test_begin_subtest "python cffi tests (NOTMUCH_CONFIG set)"
 pytest_dir=$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage
index 15c8fc6bbe0b7dd76830df509678ebd35d235228..061612193cbe8d3f8373e7a4fe26bfddc9e41359 100755 (executable)
@@ -2,7 +2,7 @@
 test_description="python bindings (notmuch test suite)"
 . $(dirname "$0")/test-lib.sh || exit 1
 
-if [ $NOTMUCH_HAVE_PYTHON3_CFFI -eq 0 -o $NOTMUCH_HAVE_PYTHON3_PYTEST -eq 0 ]; then
+if [ "${NOTMUCH_HAVE_PYTHON3_CFFI-0}" = "0" -o "${NOTMUCH_HAVE_PYTHON3_PYTEST-0}" = "0" ]; then
     test_done
 fi
 
index e828efed3dbe4bca31ccf0f134c560b021cea928..d0c6bb17bdda217e1e4aff594f77592fa04636d8 100755 (executable)
@@ -2,7 +2,7 @@
 test_description="ruby bindings"
 . $(dirname "$0")/test-lib.sh || exit 1
 
-if [ "${NOTMUCH_HAVE_RUBY_DEV}" = "0" ]; then
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" -a "${NOTMUCH_HAVE_RUBY_DEV-0}" = "0" ]; then
     test_subtest_missing_external_prereq_["ruby development files"]=t
 fi
 
@@ -12,10 +12,14 @@ test_ruby() {
     (
        cat <<-EOF
        require 'notmuch'
-       db = Notmuch::Database.new('$MAIL_DIR')
+       db = Notmuch::Database.new()
        EOF
        cat
-    ) | $NOTMUCH_RUBY -I "$NOTMUCH_BUILDDIR/bindings/ruby"> OUTPUT
+    ) | if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       ruby
+    else
+       $NOTMUCH_RUBY -I "$NOTMUCH_BUILDDIR/bindings/ruby"
+    fi> OUTPUT
     test_expect_equal_file EXPECTED OUTPUT
 }
 
index d9aa8e2d01c811b77a0373cde8105e6e32e29965..40b625feddbfce84db3934ab8db429469efcf7d0 100755 (executable)
@@ -2,6 +2,10 @@
 test_description="argument parsing"
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 test_begin_subtest "sanity check"
 $TEST_DIRECTORY/arg-test pos1 --keyword=one --boolean --string=foo pos2 --int=7 --flag=one --flag=three > OUTPUT
 cat <<EOF > EXPECTED
index 3a1c449ef2b4350dd7d08397b5213ea1efb526fc..6ef5c54ab8579dfa7a22e2d04c71487fd2d3d352 100755 (executable)
@@ -100,7 +100,7 @@ test_emacs '(notmuch-hello)
            (notmuch-test-wait)
            (test-output)
            (delete-other-windows)'
-test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox OUTPUT
+test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox-oldest-first OUTPUT
 
 test_begin_subtest "Tree view of a single thread (from search)"
 test_emacs '(notmuch-hello)
@@ -200,6 +200,30 @@ test_emacs '(test-log-error
                (notmuch-tree "*")))'
 test_expect_equal "$(cat MESSAGES)" "COMPLETE"
 
+# reinitialize database for outline tests
+add_email_corpus
+
+test_begin_subtest "start in outline mode"
+test_emacs '(let ((notmuch-tree-outline-enabled t))
+       (notmuch-tree "tag:inbox")
+       (notmuch-test-wait)
+       (test-visible-output))'
+# folding all messages by height or depth should look the same
+test_expect_equal_file $EXPECTED/inbox-outline OUTPUT
+
+test_begin_subtest "outline-cycle-buffer"
+test_emacs '(let ((notmuch-tree-outline-enabled t))
+       (notmuch-tree "tag:inbox")
+       (notmuch-test-wait)
+       (outline-cycle-buffer)
+       (outline-cycle-buffer)
+       (notmuch-test-wait)
+       (test-visible-output))'
+# folding all messages by height or depth should look the same
+test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox OUTPUT
+
+test_done
+
 add_email_corpus duplicate
 
 ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
diff --git a/test/T461-emacs-search-exclude.sh b/test/T461-emacs-search-exclude.sh
new file mode 100755 (executable)
index 0000000..47f7468
--- /dev/null
@@ -0,0 +1,185 @@
+#!/usr/bin/env bash
+
+test_description="exclude options persist between Emacs search and tree modes"
+. $(dirname "$0")/test-lib.sh || exit 1
+. $NOTMUCH_SRCDIR/test/test-lib-emacs.sh || exit 1
+
+EXPECTED=$NOTMUCH_SRCDIR/test/emacs-exclude.expected-output
+
+test_require_emacs
+add_email_corpus
+notmuch config set search.exclude_tags deleted
+notmuch tag +deleted -- 'from:"Stewart Smith"' or 'from:"Chris Wilson"'
+
+# Basic test cases just asserting exclude option is working and consistent.
+
+test_begin_subtest "Search doesn't contain excluded mail by default"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-search-tag-inbox-without-excluded OUTPUT
+
+test_begin_subtest "Toggling exclude in search will show excluded mail"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-search-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-search-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Tree search doesn't contain excluded mail by default"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox-without-excluded OUTPUT
+
+test_begin_subtest "Toggling exclude in tree search will show excluded mail"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-current-query)
+           (notmuch-test-wait)
+           (notmuch-tree-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Unthreaded search doesn't contain excluded mail by default"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-unthreaded-from-search-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-unthreaded-tag-inbox-without-excluded OUTPUT
+
+test_begin_subtest "Toggling exclude in unthreaded will show excluded mail"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-unthreaded-from-search-current-query)
+           (notmuch-test-wait)
+           (notmuch-tree-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-unthreaded-tag-inbox-with-excluded OUTPUT
+
+# Cycling from search to tree to unthreaded and vice versa will persist the current
+# value of notmuch-search-hide-excluded.
+
+test_begin_subtest "Value of hide-excluded from search persists into tree search"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-search-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Value of hide-excluded from search persists into unthreaded"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-search-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (notmuch-unthreaded-from-search-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-unthreaded-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Value of hide-excluded from tree persists into search"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-current-query)
+           (notmuch-test-wait)
+           (notmuch-tree-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (notmuch-search-from-tree-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-search-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Value of hide-excluded from tree persists into unthreaded"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-current-query)
+           (notmuch-test-wait)
+           (notmuch-tree-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (notmuch-unthreaded-from-tree-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-unthreaded-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Value of hide-excluded from unthreaded persists into tree"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-unthreaded-from-search-current-query)
+           (notmuch-test-wait)
+           (notmuch-tree-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (notmuch-tree-from-unthreaded-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-tree-tag-inbox-with-excluded OUTPUT
+
+test_begin_subtest "Value of hide-excluded from unthreaded persists into search"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-unthreaded-from-search-current-query)
+           (notmuch-test-wait)
+           (notmuch-tree-toggle-hide-excluded)
+           (notmuch-test-wait)
+           (notmuch-search-from-tree-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file $EXPECTED/notmuch-search-tag-inbox-with-excluded OUTPUT
+
+test_done
index b2eb80b901027ae2931d06f6ef9be33dac04722c..8bddf3e744c7f58d4db165b15905ccc83921b12c 100755 (executable)
@@ -2,6 +2,10 @@
 test_description="hex encoding and decoding"
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 test_begin_subtest "round trip"
 find $NOTMUCH_SRCDIR/test/corpora/default -type f -print | sort | xargs cat > EXPECTED
 $TEST_DIRECTORY/hex-xcode --direction=encode < EXPECTED | $TEST_DIRECTORY/hex-xcode --direction=decode > OUTPUT
index f89755ed0e42cfe7d87040f85410e77b8002adfd..3b6e48c42aefe040ec926c1e634bab3c9ffbc7a0 100755 (executable)
@@ -2,6 +2,10 @@
 test_description="date/time parser module"
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 # Sanity/smoke tests for the date/time parser independent of notmuch
 
 _date () {
index c7b73a6d4a2bc2a9f892732f49052e1d97052183..6bcf109c76160c32fa0380fc6d2b68deb4d37474 100755 (executable)
@@ -35,7 +35,7 @@ notmuch show --entire-thread=true --sort=oldest-first $QUERY > OUTPUT
 test_expect_equal_file EXPECTED OUTPUT
 
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
 
     test_query_syntax '(and "wonderful" "wizard")' 'wonderful and wizard'
     test_query_syntax '(or "php" "wizard")' 'php or wizard'
index 9d5a9e70a598089793d5d06b136dcbd0ae428ced..3048c7c4a5534c6a092836754ca35b33bc55fe26 100755 (executable)
@@ -3,6 +3,10 @@ test_description="database version and feature compatibility"
 
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 test_begin_subtest "future database versions abort open"
 ${TEST_DIRECTORY}/make-db-version ${MAIL_DIR} 9999 ""
 output=$(notmuch search x 2>&1 | sed 's/\(database at\) .*/\1 FILENAME/')
index 511d56ca694e5c131d4d5815ad43d3e80ee5fbb1..6905193788a4b22419897119d2e1d14d50c405bc 100755 (executable)
@@ -3,6 +3,10 @@ test_description="API tests for notmuch_message_*"
 
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED}" ]; then
+    test_done
+fi
+
 add_email_corpus
 
 test_begin_subtest "building database"
@@ -516,4 +520,31 @@ cat <<EOF > EXPECTED
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+TERMLIST_PATH=(${MAIL_DIR}/.notmuch/xapian/termlist.*)
+test_begin_subtest "remove message with corrupted db"
+backup_database
+cat c_head0 - c_tail <<'EOF' | test_private_C ${MAIL_DIR} ${TERMLIST_PATH}
+    {
+        notmuch_status_t status;
+
+        int fd = open(argv[2],O_WRONLY|O_TRUNC);
+        if (fd < 0) {
+            fprintf (stderr, "error opening %s\n", argv[1]);
+            exit (1);
+        }
+
+        stat = _notmuch_message_delete (message);
+        printf ("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+    }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+== stderr ==
+A Xapian exception occurred at message.cc:XXX: EOF reading block YYY
+EOF
+sed 's/EOF reading block [0-9]*/EOF reading block YYY/' < OUTPUT > OUTPUT.clean
+test_expect_equal_file EXPECTED OUTPUT.clean
+restore_database
+
 test_done
index a748005056d77c06ff511f8de2bf3d7333081364..bcc97dd9396305c7b984d156e575bdde87198804 100755 (executable)
@@ -95,7 +95,7 @@ subtotal=$(notmuch count lastmod:..$lastmod)
 result=$(($subtotal == $total-1))
 test_expect_equal 1 "$result"
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
     test_begin_subtest 'exclude one message using negative lastmod (sexp)'
     total=$(notmuch count '*')
     notmuch tag +${RANDOM} id:4EFC743A.3060609@april.org
index 26a1f033085bf4470cac95e9a3dd24c380b53bd9..9326ba3e3d72fa856a02458dfcfe0f67084ed1b4 100755 (executable)
@@ -440,6 +440,7 @@ cat <<'EOF' >EXPECTED
 10: 'USER_FULL_NAME'
 11: '8000'
 12: 'NULL'
+13: ''
 == stderr ==
 EOF
 unset MAILDIR
@@ -725,6 +726,7 @@ test_expect_equal_file EXPECTED OUTPUT
 test_begin_subtest "list by keys (ndlc)"
 notmuch config set search.exclude_tags "foo;bar;fub"
 notmuch config set new.ignore "sekrit_junk"
+notmuch config set index.as_text "text/"
 cat c_head2 - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL%
 {
     notmuch_config_key_t key;
@@ -751,6 +753,7 @@ cat <<'EOF' >EXPECTED
 10: 'Notmuch Test Suite'
 11: '8000'
 12: 'NULL'
+13: 'text/'
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -785,6 +788,7 @@ cat <<'EOF' >EXPECTED
 10: 'USER_FULL_NAME'
 11: '8000'
 12: 'NULL'
+13: ''
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT.clean
@@ -856,6 +860,7 @@ database.backup_dir MAIL_DIR/.notmuch/backups
 database.hook_dir MAIL_DIR/.notmuch/hooks
 database.mail_root MAIL_DIR
 database.path MAIL_DIR
+index.as_text text/
 key with spaces value, with, spaces!
 maildir.synchronize_flags true
 new.ignore sekrit_junk
index 92a70e3eb12123d4ba5a947f794727cbb7d66899..2334fcaf5266b1f297ba4495342f5c95f35aaaf8 100755 (executable)
@@ -21,6 +21,10 @@ test_description='thread breakage during reindexing'
 
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 message_a () {
     mkdir -p ${MAIL_DIR}/cur
     cat > ${MAIL_DIR}/cur/a <<EOF
index 2685f3b5f5b04c62b274c46e51475f6526587a20..a7cbe04842a78a20827c06d4b5d5f825cf0807c7 100755 (executable)
@@ -12,7 +12,7 @@ void print_properties (notmuch_message_t *message, const char *prefix, notmuch_b
     notmuch_message_properties_t *list;
     for (list = notmuch_message_get_properties (message, prefix, exact);
          notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {
-       printf("%s\n", notmuch_message_properties_value(list));
+       printf("%s = %s\n", notmuch_message_properties_key(list), notmuch_message_properties_value(list));
     }
     notmuch_message_properties_destroy (list);
 }
@@ -89,17 +89,6 @@ testkey2 = NULL
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
-test_begin_subtest "notmuch_message_remove_all_properties"
-cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
-EXPECT0(notmuch_message_remove_all_properties (message, NULL));
-print_properties (message, "", FALSE);
-EOF
-cat <<'EOF' >EXPECTED
-== stdout ==
-== stderr ==
-EOF
-test_expect_equal_file EXPECTED OUTPUT
-
 test_begin_subtest "testing string map binary search (via message properties)"
 cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
 {
@@ -157,7 +146,28 @@ print_properties (message, "testkey1", TRUE);
 EOF
 cat <<'EOF' >EXPECTED
 == stdout ==
-testvalue1
+testkey1 = testvalue1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "notmuch_message_remove_all_properties"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_remove_all_properties (message, NULL));
+EXPECT0(notmuch_database_destroy(db));
+EXPECT0(notmuch_database_open_with_config (argv[1],
+                                           NOTMUCH_DATABASE_MODE_READ_WRITE,
+                                           "", NULL, &db, &msg));
+if (msg) fputs (msg, stderr);
+EXPECT0(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message));
+if (message == NULL) {
+  fprintf (stderr, "unable to find message");
+  exit (1);
+}
+print_properties (message, "", FALSE);
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -171,10 +181,9 @@ print_properties (message, "testkey1", TRUE);
 EOF
 cat <<'EOF' >EXPECTED
 == stdout ==
-alice
-bob
-testvalue1
-testvalue2
+testkey1 = alice
+testkey1 = bob
+testkey1 = testvalue2
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -186,27 +195,14 @@ EXPECT0(notmuch_message_add_property (message, "testkey3", "testvalue3"));
 EXPECT0(notmuch_message_add_property (message, "testkey3", "alice3"));
 print_properties (message, "testkey", FALSE);
 EOF
-# expected: 4 values for testkey1, 3 values for testkey3
-# they are not guaranteed to be sorted, so sort them, leaving the first
-# line '== stdout ==' and the end ('== stderr ==' and whatever error
-# may have been printed) alone
-mv OUTPUT unsorted_OUTPUT
-awk ' NR == 1 { print; next } \
-      NR < 6  { print | "sort"; next } \
-      NR == 6 { close("sort") } \
-      NR < 9  { print | "sort"; next } \
-      NR == 9 { close("sort") } \
-      { print }' unsorted_OUTPUT > OUTPUT
-rm unsorted_OUTPUT
 cat <<'EOF' >EXPECTED
 == stdout ==
-alice
-bob
-testvalue1
-testvalue2
-alice3
-bob3
-testvalue3
+testkey1 = alice
+testkey1 = bob
+testkey1 = testvalue2
+testkey3 = alice3
+testkey3 = bob3
+testkey3 = testvalue3
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -248,7 +244,7 @@ test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "dump message properties"
 cat <<EOF > PROPERTIES
-#= 4EFC743A.3060609@april.org fancy%20key%20with%20%c3%a1cc%c3%a8nts=import%20value%20with%20= testkey1=alice testkey1=bob testkey1=testvalue1 testkey1=testvalue2 testkey3=alice3 testkey3=bob3 testkey3=testvalue3
+#= 4EFC743A.3060609@april.org fancy%20key%20with%20%c3%a1cc%c3%a8nts=import%20value%20with%20= testkey1=alice testkey1=bob testkey1=testvalue2 testkey3=alice3 testkey3=bob3 testkey3=testvalue3
 EOF
 cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
 EXPECT0(notmuch_message_add_property (message, "fancy key with áccènts", "import value with ="));
@@ -259,7 +255,7 @@ test_expect_equal_file PROPERTIES OUTPUT
 test_begin_subtest "dump _only_ message properties"
 cat <<EOF > EXPECTED
 #notmuch-dump batch-tag:3 properties
-#= 4EFC743A.3060609@april.org fancy%20key%20with%20%c3%a1cc%c3%a8nts=import%20value%20with%20= testkey1=alice testkey1=bob testkey1=testvalue1 testkey1=testvalue2 testkey3=alice3 testkey3=bob3 testkey3=testvalue3
+#= 4EFC743A.3060609@april.org fancy%20key%20with%20%c3%a1cc%c3%a8nts=import%20value%20with%20= testkey1=alice testkey1=bob testkey1=testvalue2 testkey3=alice3 testkey3=bob3 testkey3=testvalue3
 EOF
 notmuch dump --include=properties > OUTPUT
 test_expect_equal_file EXPECTED OUTPUT
@@ -328,7 +324,6 @@ EOF
 cat <<'EOF' > EXPECTED
 testkey1 = alice
 testkey1 = bob
-testkey1 = testvalue1
 testkey1 = testvalue2
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -344,7 +339,6 @@ EOF
 cat <<'EOF' > EXPECTED
 testkey1 = alice
 testkey1 = bob
-testkey1 = testvalue1
 testkey1 = testvalue2
 testkey3 = alice3
 testkey3 = bob3
@@ -362,4 +356,49 @@ for (key,val) in msg.get_properties("testkey",True):
 EOF
 test_expect_equal_file /dev/null OUTPUT
 
+test_begin_subtest "notmuch_message_remove_all_properties_with_prefix"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_remove_all_properties_with_prefix (message, "testkey3"));
+print_properties (message, "", FALSE);
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+fancy key with áccènts = import value with =
+testkey1 = alice
+testkey1 = bob
+testkey1 = testvalue2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "edit property on removed message without uncaught exception"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_database_remove_message (db, notmuch_message_get_filename (message)));
+stat = notmuch_message_remove_property (message, "example", "example");
+if (stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION)
+    fprintf (stderr, "unable to remove properties on message");
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+== stderr ==
+unable to remove properties on message
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+add_email_corpus
+
+test_begin_subtest "remove all properties on removed message without uncaught exception"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_database_remove_message (db, notmuch_message_get_filename (message)));
+stat = notmuch_message_remove_all_properties_with_prefix (message, "");
+if (stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION)
+    fprintf (stderr, "unable to remove properties on message");
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+== stderr ==
+unable to remove properties on message
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
index 347f84838fa2e7d7310488f460f4ec6344ed405a..af34ad7cb9e756636377604c3f01adff88d0bf6f 100755 (executable)
@@ -5,7 +5,7 @@ test_description='reindexing messages'
 add_email_corpus
 
 
-if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" = "1" ]; then
 
     count=$(notmuch count --lastmod '*' | cut -f 3)
     for query in '()' '(not)' '(and)' '(or ())' '(or (not))' '(or (and))' \
index 5129d84c544dc5f0b101fed30a51da0e5cd7c590..a2d8ec71ecd12c043217bc09b122d3a8a3e02dfc 100755 (executable)
@@ -3,6 +3,10 @@ test_description="message id parsing"
 
 . $(dirname "$0")/test-lib.sh || exit 1
 
+if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    test_done
+fi
+
 test_begin_subtest "good message ids"
 ${TEST_DIRECTORY}/message-id-parse <<EOF >OUTPUT
 <018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org>
diff --git a/test/T760-as-text.sh b/test/T760-as-text.sh
new file mode 100755 (executable)
index 0000000..744567f
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/env bash
+test_description='index attachments as text'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus indexing
+test_begin_subtest "empty as_text; skip text/x-diff"
+messages=$(notmuch count id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain)
+count=$(notmuch count id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz)
+test_expect_equal "$messages,$count" "1,0"
+
+notmuch config set index.as_text "^text/"
+add_email_corpus indexing
+
+test_begin_subtest "as_index is text/; find text/x-diff"
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain > EXPECTED
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "reindex with empty as_text, skips text/x-diff"
+notmuch config set index.as_text
+notmuch reindex '*'
+messages=$(notmuch count id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain)
+count=$(notmuch count id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz)
+test_expect_equal "$messages,$count" "1,0"
+
+test_begin_subtest "reindex with empty as_text; skips application/pdf"
+notmuch config set index.as_text
+notmuch reindex '*'
+gmessages=$(notmuch count id:871qo9p4tf.fsf@tethera.net)
+count=$(notmuch count id:871qo9p4tf.fsf@tethera.net and body:not-really-PDF)
+test_expect_equal "$messages,$count" "1,0"
+
+test_begin_subtest "reindex with as_text as text/; finds text/x-diff"
+notmuch config set index.as_text "^text/"
+notmuch reindex '*'
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain > EXPECTED
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "reindex with as_text as text/; skips application/pdf"
+notmuch config set index.as_text "^text/"
+notmuch config set index.as_text
+notmuch reindex '*'
+messages=$(notmuch count id:871qo9p4tf.fsf@tethera.net)
+count=$(notmuch count id:871qo9p4tf.fsf@tethera.net and body:not-really-PDF)
+test_expect_equal "$messages,$count" "1,0"
+
+test_begin_subtest "as_text has multiple regexes"
+notmuch config set index.as_text "blahblah;^text/"
+notmuch reindex '*'
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain > EXPECTED
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "as_text is non-anchored regex"
+notmuch config set index.as_text "e.t/"
+notmuch reindex '*'
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain > EXPECTED
+notmuch search id:20200930101213.2m2pt3jrspvcrxfx@localhost.localdomain and ersatz > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "as_text is 'application/pdf'"
+notmuch config set index.as_text "^application/pdf$"
+notmuch reindex '*'
+notmuch search id:871qo9p4tf.fsf@tethera.net > EXPECTED
+notmuch search id:871qo9p4tf.fsf@tethera.net and '"not really PDF"' > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "as_text is bad regex"
+notmuch config set index.as_text '['
+notmuch reindex '*' >& OUTPUT
+cat<<EOF > EXPECTED
+Error in index.as_text: Invalid regular expression: [
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
index 8607732e176b9978bd72622a6e5ddf8a255e6710..9ce6baa7c80b34e767bd8900f2f74c8498676818 100755 (executable)
@@ -2,14 +2,19 @@
 test_description='run code with ASAN enabled against the library'
 . $(dirname "$0")/test-lib.sh || exit 1
 
-if [ $NOTMUCH_HAVE_ASAN -ne 1 ]; then
+if [ "${NOTMUCH_HAVE_ASAN-0}" != "1" ]; then
     printf "Skipping due to missing ASAN support\n"
     test_done
 fi
 
+if [ -n "${LD_PRELOAD-}" ]; then
+    printf "Skipping due to ASAN LD_PRELOAD restrictions\n"
+    test_done
+fi
+
 add_email_corpus
 
-TEST_CFLAGS="-fsanitize=address"
+TEST_CFLAGS="${TEST_CFLAGS:-} -fsanitize=address"
 
 test_begin_subtest "open and destroy"
 test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} <<EOF
diff --git a/test/T810-tsan.sh b/test/T810-tsan.sh
new file mode 100755 (executable)
index 0000000..4071e29
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env bash
+
+test_directory=$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null && pwd)
+
+test_description='run code with TSan enabled against the library'
+# Note it is hard to ensure race conditions are deterministic so this
+# only provides best effort detection.
+
+. "$test_directory"/test-lib.sh || exit 1
+
+if [ "${NOTMUCH_HAVE_TSAN-0}" != "1" ]; then
+    printf "Skipping due to missing TSan support\n"
+    test_done
+fi
+
+export TSAN_OPTIONS="suppressions=$test_directory/T810-tsan.suppressions"
+TEST_CFLAGS="${TEST_CFLAGS:-} -fsanitize=thread"
+
+cp -r ${MAIL_DIR} ${MAIL_DIR}-2
+
+test_begin_subtest "create"
+test_C ${MAIL_DIR} ${MAIL_DIR}-2 <<EOF
+#include <notmuch-test.h>
+#include <pthread.h>
+
+void *thread (void *arg) {
+  char *mail_dir = arg;
+  /*
+   * Calls into notmuch_query_search_messages which was using the thread-unsafe
+   * Xapian::Query::MatchAll.
+   */
+  EXPECT0(notmuch_database_create (mail_dir, NULL));
+  return NULL;
+}
+
+int main (int argc, char **argv) {
+  pthread_t t1, t2;
+  EXPECT0(pthread_create (&t1, NULL, thread, argv[1]));
+  EXPECT0(pthread_create (&t2, NULL, thread, argv[2]));
+  EXPECT0(pthread_join (t1, NULL));
+  EXPECT0(pthread_join (t2, NULL));
+  return 0;
+}
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+add_email_corpus
+rm -r ${MAIL_DIR}-2
+cp -r ${MAIL_DIR} ${MAIL_DIR}-2
+
+test_begin_subtest "query"
+test_C ${MAIL_DIR} ${MAIL_DIR}-2 <<EOF
+#include <notmuch-test.h>
+#include <pthread.h>
+
+void *thread (void *arg) {
+  char *mail_dir = arg;
+  notmuch_database_t *db;
+  /*
+   * 'from' is NOTMUCH_FIELD_PROBABILISTIC | NOTMUCH_FIELD_PROCESSOR and an
+   * empty string gets us to RegexpFieldProcessor::operator which was using
+   * the tread-unsafe Xapian::Query::MatchAll.
+   */
+  EXPECT0(notmuch_database_open_with_config (mail_dir,
+                                             NOTMUCH_DATABASE_MODE_READ_ONLY,
+                                             NULL, NULL, &db, NULL));
+  notmuch_query_t *query = notmuch_query_create (db, "from:\"\"");
+  notmuch_messages_t *messages;
+  EXPECT0(notmuch_query_search_messages (query, &messages));
+  return NULL;
+}
+
+int main (int argc, char **argv) {
+  pthread_t t1, t2;
+  EXPECT0(pthread_create (&t1, NULL, thread, argv[1]));
+  EXPECT0(pthread_create (&t2, NULL, thread, argv[2]));
+  EXPECT0(pthread_join (t1, NULL));
+  EXPECT0(pthread_join (t2, NULL));
+  return 0;
+}
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
diff --git a/test/T810-tsan.suppressions b/test/T810-tsan.suppressions
new file mode 100644 (file)
index 0000000..80dc062
--- /dev/null
@@ -0,0 +1,3 @@
+# It's unclear how TSan-friendly GLib is:
+# https://gitlab.gnome.org/GNOME/glib/-/issues/1672
+called_from_lib:libglib*.so
index 55cec78a16391b3f01e4139d5086729ca7f94bfb..831e4678ef18a1ddc3c13378a0ae6d46d757d062 100755 (executable)
@@ -2,7 +2,7 @@
 test_description='"notmuch git" to save and restore tags'
 . $(dirname "$0")/test-lib.sh || exit 1
 
-if [ $NOTMUCH_HAVE_SFSEXP -ne 1 ]; then
+if [ "${NOTMUCH_HAVE_SFSEXP-0}" != "1" ]; then
     printf "Skipping due to missing sfsexp library\n"
     test_done
 fi
@@ -233,6 +233,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "invoke as nmbug sets defaults"
+test_subtest_broken_for_installed
 "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^\(prefix\|repository\)' | notmuch_dir_sanitize > OUTPUT
 cat <<EOF > EXPECTED
 prefix = notmuch::
@@ -241,6 +242,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "env variable NOTMUCH_GIT_DIR works when invoked as nmbug"
+test_subtest_broken_for_installed
 NOTMUCH_GIT_DIR=`pwd`/foo "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
 cat <<EOF > EXPECTED
 repository = CWD/foo
@@ -256,6 +258,7 @@ test_expect_equal_file EXPECTED OUTPUT
 
 
 test_begin_subtest "env variable NOTMUCH_GIT_DIR overrides config when invoked as 'nmbug'"
+test_subtest_broken_for_installed
 notmuch config set git.path `pwd`/bar
 NOTMUCH_GIT_DIR=`pwd`/remote.git  "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
 notmuch config set git.path
@@ -274,6 +277,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "env variable NOTMUCH_GIT_PREFIX works when invoked as 'nmbug'"
+test_subtest_broken_for_installed
 NOTMUCH_GIT_PREFIX=env:: "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
 cat <<EOF > EXPECTED
 prefix = env::
@@ -281,6 +285,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "env variable NOTMUCH_GIT_PREFIX works when invoked as nmbug"
+test_subtest_broken_for_installed
 NOTMUCH_GIT_PREFIX=foo:: "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
 cat <<EOF > EXPECTED
 prefix = foo::
@@ -288,6 +293,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "env variable NOTMUCH_GIT_PREFIX overrides config when invoked as 'nmbug'"
+test_subtest_broken_for_installed
 notmuch config set git.tag_prefix config::
 NOTMUCH_GIT_PREFIX=env:: "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
 notmuch config set git.path
diff --git a/test/corpora/indexing/fake-pdf:2,S b/test/corpora/indexing/fake-pdf:2,S
new file mode 100644 (file)
index 0000000..60a7a47
--- /dev/null
@@ -0,0 +1,11 @@
+From: David Bremner <david@tethera.net>
+To: example@example.com
+Subject: attachment content type
+Date: Thu, 05 Jan 2023 08:02:36 -0400
+Message-ID: <871qo9p4tf.fsf@tethera.net>
+MIME-Version: 1.0
+Content-Type: application/pdf
+Content-Disposition: attachment; filename=fake.pdf
+Content-Transfer-Encoding: base64
+
+dGhpcyBpcyBub3QgcmVhbGx5IFBERgo=
\ No newline at end of file
diff --git a/test/emacs-exclude.expected-output/notmuch-search-tag-inbox-with-excluded b/test/emacs-exclude.expected-output/notmuch-search-tag-inbox-with-excluded
new file mode 100644 (file)
index 0000000..ce1d711
--- /dev/null
@@ -0,0 +1,25 @@
+  2009-11-17 [5/5]   Mikhail Gusarov, Carl Worth, Keith Packard  [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17 [7/7]   Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth  [notmuch] Working with Maildir storage? (inbox signed unread)
+  2009-11-17 [2/2]   Alex Botero-Lowry, Carl Worth  [notmuch] preliminary FreeBSD support (attachment inbox unread)
+  2009-11-17 [1/1]   Mikhail Gusarov      [notmuch] [PATCH] Handle rename of message file (inbox unread)
+  2009-11-17 [2/2]   Keith Packard, Carl Worth    [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-17 [2/2]   Jan Janak, Carl Worth        [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-17 [3/3]   Jan Janak, Carl Worth        [notmuch] What a great idea! (inbox unread)
+  2009-11-17 [3/3]   Israel Herraiz, Keith Packard, Carl Worth   [notmuch] New to the list (inbox unread)
+  2009-11-17 [3/3]   Adrian Perez de Castro, Keith Packard, Carl Worth  [notmuch] Introducing myself (inbox signed unread)
+  2009-11-17 [3/3]   Aron Griffis, Keith Packard, Carl Worth     [notmuch] archive (inbox unread)
+  2009-11-17 [2/2]   Ingmar Vanhassel, Carl Worth  [notmuch] [PATCH] Typsos (inbox unread)
+  2009-11-18 [2/2]   Alex Botero-Lowry, Carl Worth  [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18 [2/2]   Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (deleted inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH 2/2] Read mail directory in inode number order (deleted inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] count_files: sort directory in inode order before statting (deleted inbox unread)
+  2009-11-18 [4/4]   Jjgod Jiang, Alexander Botero-Lowry      [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
+  2009-11-18 [1/1]   Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (inbox unread)
+  2009-11-18 [1/1]   Rolland Santimano    [notmuch] Link to mailing list archives ? (inbox unread)
+  2009-11-18 [1/1]   Alexander Botero-Lowry  [notmuch] request for pull (inbox unread)
+  2009-11-18 [2/2]   Keith Packard, Alexander Botero-Lowry    [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18 [1/1]   Chris Wilson         [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (deleted inbox unread)
+  2010-12-16 [1/1]   Olivier Berger       Essai accentué (inbox unread)
+  2010-12-29 [1/1]   François Boulogne    [aur-general] Guidelines: cp, mkdir vs install (inbox unread)
+End of search results.
diff --git a/test/emacs-exclude.expected-output/notmuch-search-tag-inbox-without-excluded b/test/emacs-exclude.expected-output/notmuch-search-tag-inbox-without-excluded
new file mode 100644 (file)
index 0000000..8a87432
--- /dev/null
@@ -0,0 +1,21 @@
+  2009-11-17 [5/5]   Mikhail Gusarov, Carl Worth, Keith Packard  [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17 [7/7]   Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth  [notmuch] Working with Maildir storage? (inbox signed unread)
+  2009-11-17 [2/2]   Alex Botero-Lowry, Carl Worth  [notmuch] preliminary FreeBSD support (attachment inbox unread)
+  2009-11-17 [1/1]   Mikhail Gusarov      [notmuch] [PATCH] Handle rename of message file (inbox unread)
+  2009-11-17 [2/2]   Keith Packard, Carl Worth    [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-17 [2/2]   Jan Janak, Carl Worth        [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-17 [3/3]   Jan Janak, Carl Worth        [notmuch] What a great idea! (inbox unread)
+  2009-11-17 [3/3]   Israel Herraiz, Keith Packard, Carl Worth   [notmuch] New to the list (inbox unread)
+  2009-11-17 [3/3]   Adrian Perez de Castro, Keith Packard, Carl Worth  [notmuch] Introducing myself (inbox signed unread)
+  2009-11-17 [3/3]   Aron Griffis, Keith Packard, Carl Worth     [notmuch] archive (inbox unread)
+  2009-11-17 [2/2]   Ingmar Vanhassel, Carl Worth  [notmuch] [PATCH] Typsos (inbox unread)
+  2009-11-18 [2/2]   Alex Botero-Lowry, Carl Worth  [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18 [2/2]   Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)
+  2009-11-18 [4/4]   Jjgod Jiang, Alexander Botero-Lowry      [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
+  2009-11-18 [1/1]   Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (inbox unread)
+  2009-11-18 [1/1]   Rolland Santimano    [notmuch] Link to mailing list archives ? (inbox unread)
+  2009-11-18 [1/1]   Alexander Botero-Lowry  [notmuch] request for pull (inbox unread)
+  2009-11-18 [2/2]   Keith Packard, Alexander Botero-Lowry    [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2010-12-16 [1/1]   Olivier Berger       Essai accentué (inbox unread)
+  2010-12-29 [1/1]   François Boulogne    [aur-general] Guidelines: cp, mkdir vs install (inbox unread)
+End of search results.
diff --git a/test/emacs-exclude.expected-output/notmuch-tree-tag-inbox-with-excluded b/test/emacs-exclude.expected-output/notmuch-tree-tag-inbox-with-excluded
new file mode 100644 (file)
index 0000000..5c6b2d7
--- /dev/null
@@ -0,0 +1,53 @@
+  2009-11-17  Mikhail Gusarov       ┬►[notmuch] [PATCH 1/2] Close message file after parsing message       headers (inbox unread)
+  2009-11-17  Mikhail Gusarov       ├─►[notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++   file with gcc 4.4 (inbox unread)
+  2009-11-17  Carl Worth            ╰┬►[notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Keith Packard          ╰┬► ...                                              (inbox unread)
+  2009-11-18  Carl Worth              ╰─► ...                                             (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman  ┬►[notmuch] Working with Maildir storage?             (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       ├┬► ...                                               (inbox signed unread)
+  2009-11-17  Lars Kellogg-Stedman  │╰┬► ...                                              (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       │ ├─► ...                                             (inbox unread)
+  2009-11-17  Keith Packard         │ ╰┬► ...                                             (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  │  ╰─► ...                                            (inbox signed unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Alex Botero-Lowry     ┬►[notmuch] preliminary FreeBSD support               (attachment inbox unread)
+  2009-11-17  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Mikhail Gusarov       ─►[notmuch] [PATCH] Handle rename of message file     (inbox unread)
+  2009-11-17  Keith Packard         ┬►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove    inbox (and unread) tags (inbox unread)
+  2009-11-18  Carl Worth            ╰─►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] What a great idea!                        (inbox unread)
+  2009-11-17  Jan Janak             ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Israel Herraiz        ┬►[notmuch] New to the list                           (inbox unread)
+  2009-11-18  Keith Packard         ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Adrian Perez de Cast  ┬►[notmuch] Introducing myself                        (inbox signed unread)
+  2009-11-18  Keith Packard         ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Aron Griffis          ┬►[notmuch] archive                                   (inbox unread)
+  2009-11-18  Keith Packard         ╰┬► ...                                               (inbox unread)
+  2009-11-18  Carl Worth             ╰─► ...                                              (inbox unread)
+  2009-11-17  Ingmar Vanhassel      ┬►[notmuch] [PATCH] Typsos                            (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-18  Alex Botero-Lowry     ┬►[notmuch] [PATCH] Error out if no query is supplied to search        instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18  Carl Worth            ╰─►[notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  ┬►[notmuch] "notmuch help" outputs to stderr?         (attachment inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman  ╰─► ...                                               (attachment inbox signed unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++     libs. (deleted inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH 2/2] Read mail directory in inode number order (deleted inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH] count_files: sort directory in inode order before  statting (deleted inbox unread)
+  2009-11-18  Jjgod Jiang           ┬►[notmuch] Mac OS X/Darwin compatibility issues      (inbox unread)
+  2009-11-18  Alexander Botero-Low  ╰┬► ...                                               (inbox unread)
+  2009-11-18  Jjgod Jiang            ╰┬► ...                                              (inbox unread)
+  2009-11-18  Alexander Botero-Low    ╰─► ...                                             (inbox unread)
+  2009-11-18  Jan Janak             ─►[notmuch] [PATCH] notmuch new: Support for conversion of spool       subdirectories into tags (inbox unread)
+  2009-11-18  Rolland Santimano     ─►[notmuch] Link to mailing list archives ?           (inbox unread)
+  2009-11-18  Alexander Botero-Low  ─►[notmuch] request for pull                          (inbox unread)
+  2009-11-18  Keith Packard         ┬►[notmuch] [PATCH] Create a default notmuch-show-hook that    highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Alexander Botero-Low  ╰─►[notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Chris Wilson          ─►[notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (deleted inbox unread)
+  2010-12-16  Olivier Berger        ─►Essai accentué                                      (inbox unread)
+  2010-12-29  François Boulogne     ─►[aur-general] Guidelines: cp, mkdir vs install      (inbox unread)
+End of search results.
diff --git a/test/emacs-exclude.expected-output/notmuch-tree-tag-inbox-without-excluded b/test/emacs-exclude.expected-output/notmuch-tree-tag-inbox-without-excluded
new file mode 100644 (file)
index 0000000..55806d1
--- /dev/null
@@ -0,0 +1,49 @@
+  2009-11-17  Mikhail Gusarov       ┬►[notmuch] [PATCH 1/2] Close message file after parsing message       headers (inbox unread)
+  2009-11-17  Mikhail Gusarov       ├─►[notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++   file with gcc 4.4 (inbox unread)
+  2009-11-17  Carl Worth            ╰┬►[notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Keith Packard          ╰┬► ...                                              (inbox unread)
+  2009-11-18  Carl Worth              ╰─► ...                                             (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman  ┬►[notmuch] Working with Maildir storage?             (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       ├┬► ...                                               (inbox signed unread)
+  2009-11-17  Lars Kellogg-Stedman  │╰┬► ...                                              (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       │ ├─► ...                                             (inbox unread)
+  2009-11-17  Keith Packard         │ ╰┬► ...                                             (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  │  ╰─► ...                                            (inbox signed unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Alex Botero-Lowry     ┬►[notmuch] preliminary FreeBSD support               (attachment inbox unread)
+  2009-11-17  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Mikhail Gusarov       ─►[notmuch] [PATCH] Handle rename of message file     (inbox unread)
+  2009-11-17  Keith Packard         ┬►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove    inbox (and unread) tags (inbox unread)
+  2009-11-18  Carl Worth            ╰─►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] What a great idea!                        (inbox unread)
+  2009-11-17  Jan Janak             ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Israel Herraiz        ┬►[notmuch] New to the list                           (inbox unread)
+  2009-11-18  Keith Packard         ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Adrian Perez de Cast  ┬►[notmuch] Introducing myself                        (inbox signed unread)
+  2009-11-18  Keith Packard         ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Aron Griffis          ┬►[notmuch] archive                                   (inbox unread)
+  2009-11-18  Keith Packard         ╰┬► ...                                               (inbox unread)
+  2009-11-18  Carl Worth             ╰─► ...                                              (inbox unread)
+  2009-11-17  Ingmar Vanhassel      ┬►[notmuch] [PATCH] Typsos                            (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-18  Alex Botero-Lowry     ┬►[notmuch] [PATCH] Error out if no query is supplied to search        instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18  Carl Worth            ╰─►[notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  ┬►[notmuch] "notmuch help" outputs to stderr?         (attachment inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman  ╰─► ...                                               (attachment inbox signed unread)
+  2009-11-18  Jjgod Jiang           ┬►[notmuch] Mac OS X/Darwin compatibility issues      (inbox unread)
+  2009-11-18  Alexander Botero-Low  ╰┬► ...                                               (inbox unread)
+  2009-11-18  Jjgod Jiang            ╰┬► ...                                              (inbox unread)
+  2009-11-18  Alexander Botero-Low    ╰─► ...                                             (inbox unread)
+  2009-11-18  Jan Janak             ─►[notmuch] [PATCH] notmuch new: Support for conversion of spool       subdirectories into tags (inbox unread)
+  2009-11-18  Rolland Santimano     ─►[notmuch] Link to mailing list archives ?           (inbox unread)
+  2009-11-18  Alexander Botero-Low  ─►[notmuch] request for pull                          (inbox unread)
+  2009-11-18  Keith Packard         ┬►[notmuch] [PATCH] Create a default notmuch-show-hook that    highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Alexander Botero-Low  ╰─►[notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2010-12-16  Olivier Berger        ─►Essai accentué                                      (inbox unread)
+  2010-12-29  François Boulogne     ─►[aur-general] Guidelines: cp, mkdir vs install      (inbox unread)
+End of search results.
diff --git a/test/emacs-exclude.expected-output/notmuch-unthreaded-tag-inbox-with-excluded b/test/emacs-exclude.expected-output/notmuch-unthreaded-tag-inbox-with-excluded
new file mode 100644 (file)
index 0000000..d55818e
--- /dev/null
@@ -0,0 +1,53 @@
+  2009-11-17  Mikhail Gusarov      [notmuch] [PATCH 1/2] Close message file after parsing message      headers (inbox unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++     file with gcc 4.4 (inbox unread)
+  2009-11-17  Carl Worth           [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-17  Alex Botero-Lowry    [notmuch] preliminary FreeBSD support                  (attachment inbox unread)
+  2009-11-17  Carl Worth           [notmuch] preliminary FreeBSD support                  (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] Working with Maildir storage?                (inbox unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] [PATCH] Handle rename of message file        (inbox unread)
+  2009-11-17  Keith Packard        [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Keith Packard        [notmuch] Working with Maildir storage?                (inbox unread)
+  2009-11-17  Keith Packard        [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove   inbox (and unread) tags (inbox unread)
+  2009-11-17  Jan Janak            [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-17  Jan Janak            [notmuch] What a great idea!                           (inbox unread)
+  2009-11-17  Jan Janak            [notmuch] What a great idea!                           (inbox unread)
+  2009-11-17  Israel Herraiz       [notmuch] New to the list                              (inbox unread)
+  2009-11-17  Adrian Perez de Cast [notmuch] Introducing myself                           (inbox signed unread)
+  2009-11-17  Aron Griffis         [notmuch] archive                                      (inbox unread)
+  2009-11-17  Ingmar Vanhassel     [notmuch] [PATCH] Typsos                               (inbox unread)
+  2009-11-18  Alex Botero-Lowry    [notmuch] [PATCH] Error out if no query is supplied to search       instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18  Lars Kellogg-Stedman [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr?            (attachment inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr?            (attachment inbox signed unread)
+  2009-11-18  Stewart Smith        [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++    libs. (deleted inbox unread)
+  2009-11-18  Stewart Smith        [notmuch] [PATCH 2/2] Read mail directory in inode number order (deleted inbox unread)
+  2009-11-18  Keith Packard        [notmuch] New to the list                              (inbox unread)
+  2009-11-18  Keith Packard        [notmuch] Introducing myself                           (inbox unread)
+  2009-11-18  Keith Packard        [notmuch] archive                                      (inbox unread)
+  2009-11-18  Stewart Smith        [notmuch] [PATCH] count_files: sort directory in inode order before statting (deleted inbox unread)
+  2009-11-18  Jjgod Jiang          [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool      subdirectories into tags (inbox unread)
+  2009-11-18  Rolland Santimano    [notmuch] Link to mailing list archives ?              (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Jjgod Jiang          [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] request for pull                             (inbox unread)
+  2009-11-18  Keith Packard        [notmuch] [PATCH] Create a default notmuch-show-hook that   highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] Working with Maildir storage?                (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] archive                                      (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] What a great idea!                           (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] New to the list                              (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] Introducing myself                           (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Typsos                               (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (inbox unread)
+  2009-11-18  Chris Wilson         [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (deleted inbox unread)
+  2010-12-16  Olivier Berger       Essai accentué                                         (inbox unread)
+  2010-12-29  François Boulogne    [aur-general] Guidelines: cp, mkdir vs install         (inbox unread)
+End of search results.
diff --git a/test/emacs-exclude.expected-output/notmuch-unthreaded-tag-inbox-without-excluded b/test/emacs-exclude.expected-output/notmuch-unthreaded-tag-inbox-without-excluded
new file mode 100644 (file)
index 0000000..80c67d0
--- /dev/null
@@ -0,0 +1,49 @@
+  2009-11-17  Mikhail Gusarov      [notmuch] [PATCH 1/2] Close message file after parsing message      headers (inbox unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++     file with gcc 4.4 (inbox unread)
+  2009-11-17  Carl Worth           [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-17  Alex Botero-Lowry    [notmuch] preliminary FreeBSD support                  (attachment inbox unread)
+  2009-11-17  Carl Worth           [notmuch] preliminary FreeBSD support                  (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] Working with Maildir storage?                (inbox unread)
+  2009-11-17  Mikhail Gusarov      [notmuch] [PATCH] Handle rename of message file        (inbox unread)
+  2009-11-17  Keith Packard        [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Keith Packard        [notmuch] Working with Maildir storage?                (inbox unread)
+  2009-11-17  Keith Packard        [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove   inbox (and unread) tags (inbox unread)
+  2009-11-17  Jan Janak            [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-17  Jan Janak            [notmuch] What a great idea!                           (inbox unread)
+  2009-11-17  Jan Janak            [notmuch] What a great idea!                           (inbox unread)
+  2009-11-17  Israel Herraiz       [notmuch] New to the list                              (inbox unread)
+  2009-11-17  Adrian Perez de Cast [notmuch] Introducing myself                           (inbox signed unread)
+  2009-11-17  Aron Griffis         [notmuch] archive                                      (inbox unread)
+  2009-11-17  Ingmar Vanhassel     [notmuch] [PATCH] Typsos                               (inbox unread)
+  2009-11-18  Alex Botero-Lowry    [notmuch] [PATCH] Error out if no query is supplied to search       instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18  Lars Kellogg-Stedman [notmuch] Working with Maildir storage?                (inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr?            (attachment inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr?            (attachment inbox signed unread)
+  2009-11-18  Keith Packard        [notmuch] New to the list                              (inbox unread)
+  2009-11-18  Keith Packard        [notmuch] Introducing myself                           (inbox unread)
+  2009-11-18  Keith Packard        [notmuch] archive                                      (inbox unread)
+  2009-11-18  Jjgod Jiang          [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool      subdirectories into tags (inbox unread)
+  2009-11-18  Rolland Santimano    [notmuch] Link to mailing list archives ?              (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Jjgod Jiang          [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] Mac OS X/Darwin compatibility issues         (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] request for pull                             (inbox unread)
+  2009-11-18  Keith Packard        [notmuch] [PATCH] Create a default notmuch-show-hook that   highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Alexander Botero-Low [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] Working with Maildir storage?                (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] archive                                      (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] What a great idea!                           (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] New to the list                              (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] Introducing myself                           (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Typsos                               (inbox unread)
+  2009-11-18  Carl Worth           [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (inbox unread)
+  2010-12-16  Olivier Berger       Essai accentué                                         (inbox unread)
+  2010-12-29  François Boulogne    [aur-general] Guidelines: cp, mkdir vs install         (inbox unread)
+End of search results.
diff --git a/test/emacs-tree.expected-output/inbox-outline b/test/emacs-tree.expected-output/inbox-outline
new file mode 100644 (file)
index 0000000..9119a91
--- /dev/null
@@ -0,0 +1,25 @@
+  2010-12-29  François Boulogne     ─►[aur-general] Guidelines: cp, mkdir vs install      (inbox unread)
+  2010-12-16  Olivier Berger        ─►Essai accentué                                      (inbox unread)
+  2009-11-18  Chris Wilson          ─►[notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread)
+  2009-11-18  Alex Botero-Lowry     ┬►[notmuch] [PATCH] Error out if no query is supplied to search        instead of going into an infinite loop (attachment inbox unread)
+  2009-11-17  Ingmar Vanhassel      ┬►[notmuch] [PATCH] Typsos                            (inbox unread)
+  2009-11-17  Adrian Perez de Cast  ┬►[notmuch] Introducing myself                        (inbox signed unread)
+  2009-11-17  Israel Herraiz        ┬►[notmuch] New to the list                           (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] What a great idea!                        (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-17  Aron Griffis          ┬►[notmuch] archive                                   (inbox unread)
+  2009-11-17  Keith Packard         ┬►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove    inbox (and unread) tags (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman  ┬►[notmuch] Working with Maildir storage?             (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       ┬►[notmuch] [PATCH 1/2] Close message file after parsing message       headers (inbox unread)
+  2009-11-18  Keith Packard         ┬►[notmuch] [PATCH] Create a default notmuch-show-hook that    highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Alexander Botero-Low  ─►[notmuch] request for pull                          (inbox unread)
+  2009-11-18  Jjgod Jiang           ┬►[notmuch] Mac OS X/Darwin compatibility issues      (inbox unread)
+  2009-11-18  Rolland Santimano     ─►[notmuch] Link to mailing list archives ?           (inbox unread)
+  2009-11-18  Jan Janak             ─►[notmuch] [PATCH] notmuch new: Support for conversion of spool       subdirectories into tags (inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH] count_files: sort directory in inode order before  statting (inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++     libs. (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  ┬►[notmuch] "notmuch help" outputs to stderr?         (attachment inbox signed unread)
+  2009-11-17  Mikhail Gusarov       ─►[notmuch] [PATCH] Handle rename of message file     (inbox unread)
+  2009-11-17  Alex Botero-Lowry     ┬►[notmuch] preliminary FreeBSD support               (attachment inbox unread)
+End of search results.
diff --git a/test/emacs-tree.expected-output/notmuch-tree-tag-inbox-oldest-first b/test/emacs-tree.expected-output/notmuch-tree-tag-inbox-oldest-first
new file mode 100644 (file)
index 0000000..588fc58
--- /dev/null
@@ -0,0 +1,53 @@
+  2009-11-17  Mikhail Gusarov       ┬►[notmuch] [PATCH 1/2] Close message file after parsing message       headers (inbox unread)
+  2009-11-17  Mikhail Gusarov       ├─►[notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++   file with gcc 4.4 (inbox unread)
+  2009-11-17  Carl Worth            ╰┬►[notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17  Keith Packard          ╰┬► ...                                              (inbox unread)
+  2009-11-18  Carl Worth              ╰─► ...                                             (inbox unread)
+  2009-11-17  Lars Kellogg-Stedman  ┬►[notmuch] Working with Maildir storage?             (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       ├┬► ...                                               (inbox signed unread)
+  2009-11-17  Lars Kellogg-Stedman  │╰┬► ...                                              (inbox signed unread)
+  2009-11-17  Mikhail Gusarov       │ ├─► ...                                             (inbox unread)
+  2009-11-17  Keith Packard         │ ╰┬► ...                                             (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  │  ╰─► ...                                            (inbox signed unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Alex Botero-Lowry     ┬►[notmuch] preliminary FreeBSD support               (attachment inbox unread)
+  2009-11-17  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Mikhail Gusarov       ─►[notmuch] [PATCH] Handle rename of message file     (inbox unread)
+  2009-11-17  Keith Packard         ┬►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove    inbox (and unread) tags (inbox unread)
+  2009-11-18  Carl Worth            ╰─►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Jan Janak             ┬►[notmuch] What a great idea!                        (inbox unread)
+  2009-11-17  Jan Janak             ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Israel Herraiz        ┬►[notmuch] New to the list                           (inbox unread)
+  2009-11-18  Keith Packard         ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Adrian Perez de Cast  ┬►[notmuch] Introducing myself                        (inbox signed unread)
+  2009-11-18  Keith Packard         ├─► ...                                               (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-17  Aron Griffis          ┬►[notmuch] archive                                   (inbox unread)
+  2009-11-18  Keith Packard         ╰┬► ...                                               (inbox unread)
+  2009-11-18  Carl Worth             ╰─► ...                                              (inbox unread)
+  2009-11-17  Ingmar Vanhassel      ┬►[notmuch] [PATCH] Typsos                            (inbox unread)
+  2009-11-18  Carl Worth            ╰─► ...                                               (inbox unread)
+  2009-11-18  Alex Botero-Lowry     ┬►[notmuch] [PATCH] Error out if no query is supplied to search        instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18  Carl Worth            ╰─►[notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (inbox unread)
+  2009-11-18  Lars Kellogg-Stedman  ┬►[notmuch] "notmuch help" outputs to stderr?         (attachment inbox signed unread)
+  2009-11-18  Lars Kellogg-Stedman  ╰─► ...                                               (attachment inbox signed unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++     libs. (inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread)
+  2009-11-18  Stewart Smith         ─►[notmuch] [PATCH] count_files: sort directory in inode order before  statting (inbox unread)
+  2009-11-18  Jjgod Jiang           ┬►[notmuch] Mac OS X/Darwin compatibility issues      (inbox unread)
+  2009-11-18  Alexander Botero-Low  ╰┬► ...                                               (inbox unread)
+  2009-11-18  Jjgod Jiang            ╰┬► ...                                              (inbox unread)
+  2009-11-18  Alexander Botero-Low    ╰─► ...                                             (inbox unread)
+  2009-11-18  Jan Janak             ─►[notmuch] [PATCH] notmuch new: Support for conversion of spool       subdirectories into tags (inbox unread)
+  2009-11-18  Rolland Santimano     ─►[notmuch] Link to mailing list archives ?           (inbox unread)
+  2009-11-18  Alexander Botero-Low  ─►[notmuch] request for pull                          (inbox unread)
+  2009-11-18  Keith Packard         ┬►[notmuch] [PATCH] Create a default notmuch-show-hook that    highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Alexander Botero-Low  ╰─►[notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18  Chris Wilson          ─►[notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread)
+  2010-12-16  Olivier Berger        ─►Essai accentué                                      (inbox unread)
+  2010-12-29  François Boulogne     ─►[aur-general] Guidelines: cp, mkdir vs install      (inbox unread)
+End of search results.
index 0a048e1fe66940b914182c78400ad4dea8a8a94c..9bc99a2cf3588fcb2dbc3ac32933b9e425ebd7f6 100644 (file)
@@ -24,7 +24,7 @@ find_builddir () {
 if [[ -z "${NOTMUCH_BUILDDIR}" ]]; then
        export NOTMUCH_BUILDDIR="$(find_builddir "$(pwd)")"
 
-       if [[ -z "${NOTMUCH_BUILDDIR}" ]]; then
+       if [ -z "${NOTMUCH_BUILDDIR}" -a "${NOTMUCH_TEST_INSTALLED-0}" = "0" ]; then
                echo "Run tests in a subdir of built notmuch tree." >&2
                exit 1
        fi
index 14747bdbfc44d040cc0118d3d84328a7c69e6e06..5d27e4d1d143c74daa4b3921a151876e8d441ce0 100755 (executable)
@@ -20,6 +20,14 @@ fi
 
 set -eu
 
+# Where to run the tests
+# XXX FIXME this code is duplicated with test-lib.sh
+if [[ -n "${NOTMUCH_BUILDDIR}" ]]; then
+    TEST_DIRECTORY=$NOTMUCH_BUILDDIR/test
+else
+    TEST_DIRECTORY=$NOTMUCH_SRCDIR/test
+fi
+
 TESTS=
 for test in ${NOTMUCH_TESTS-}; do
     TESTS="$TESTS $NOTMUCH_SRCDIR/test/$test"
@@ -80,7 +88,7 @@ for file in $TESTS
 do
     file=${file##*/} # drop leading path components
     file=${file%.sh} # drop trailing '.sh'
-    RESULT_FILES="$RESULT_FILES $NOTMUCH_BUILDDIR/test/test-results/$file"
+    RESULT_FILES="$RESULT_FILES $TEST_DIRECTORY/test-results/$file"
 done
 
 echo
@@ -94,6 +102,6 @@ if [ -n "$META_FAILURE" ]; then
 fi
 
 # Clean up
-rm -rf $NOTMUCH_BUILDDIR/test/test-results
+rm -rf $TEST_DIRECTORY/test-results
 
 exit $ev
index d8397714874cbc4236422aa61bdda53f7e322205..d925acea352e41dd3133d4062cdf4ac025a075e8 100644 (file)
@@ -1,7 +1,6 @@
 # .notmuch-config - Configuration file for the notmuch mail system
 #
 # For more information about notmuch, see https://notmuchmail.org
-
 # Database configuration
 #
 # The only value supported here is 'path' which should be the top-level
@@ -12,7 +11,6 @@
 #
 [database]
 path=/path/to/maildir
-
 # User configuration
 #
 # Here is where you can let notmuch know how you would like to be
@@ -32,7 +30,6 @@ path=/path/to/maildir
 name=Test Suite
 primary_email=test.suite@example.com
 other_email=another.suite@example.com
-
 # Configuration for "notmuch new"
 #
 # The following options are supported here:
@@ -49,7 +46,6 @@ other_email=another.suite@example.com
 #
 [new]
 tags=foo;bar;
-
 # Search configuration
 #
 # The following option is supported here:
@@ -61,7 +57,6 @@ tags=foo;bar;
 #
 [search]
 exclude_tags=baz
-
 # Maildir compatibility configuration
 #
 # The following option is supported here:
index 18fa29c0dd86d3b7eb8ab5c3b69bd7f9a35cf89a..f5d72e12c5c4873700c7814bf550cd0510c1a15c 100644 (file)
@@ -24,7 +24,7 @@
 #
 type die >/dev/null 2>&1 || die () { echo "$@" >&2; exit 1; }
 
-if [[ -z "$NOTMUCH_SRCDIR" ]] || [[ -z "$NOTMUCH_BUILDDIR" ]]; then
+if [[ -z "$NOTMUCH_SRCDIR" ]] || [ -z "${NOTMUCH_TEST_INSTALLED-}" -a -z "$NOTMUCH_BUILDDIR" ]; then
        echo "internal: srcdir or builddir not set" >&2
        exit 1
 fi
@@ -61,7 +61,9 @@ LD_LIBRARY_PATH=${TEST_DIRECTORY%/*}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
 export LD_LIBRARY_PATH
 
 # configure output
-. "$NOTMUCH_BUILDDIR/sh.config" || exit 1
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" ]; then
+   . "$NOTMUCH_BUILDDIR/sh.config" || exit 1
+fi
 
 # load OS specifics
 if [[ -e "$NOTMUCH_SRCDIR/test/test-lib-$PLATFORM.sh" ]]; then
@@ -315,7 +317,12 @@ export PATH MANPATH
 
 # Test repository
 test="tmp.$(basename "$0" .sh)"
-TMP_DIRECTORY="$TEST_DIRECTORY/$test"
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" ]; then
+    TMP_DIRECTORY="$TEST_DIRECTORY/$test"
+else
+    TMP_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-$test.XXXXXX")
+fi
+
 test ! -z "$debug" || remove_tmp=$TMP_DIRECTORY
 rm -rf "$TMP_DIRECTORY" || {
        GIT_EXIT_OK=t
@@ -323,6 +330,10 @@ rm -rf "$TMP_DIRECTORY" || {
        exit 1
 }
 
+# Provide a guess at a usable Python, to support running tests without
+# running configure first.
+NOTMUCH_PYTHON=${NOTMUCH_PYTHON-python3}
+
 # A temporary home directory is needed by at least:
 # - emacs/"Sending a message via (fake) SMTP"
 # - emacs/"Reply within emacs"
index ad4c4aeb621afd02d5d3b4cf71b34cbe2f5b6d4e..0ab58fc2c4f12c5b311b47f359edf7b90987aa8a 100644 (file)
@@ -30,6 +30,7 @@ test_require_emacs () {
 # to the message and encrypting/signing.
 emacs_deliver_message () {
     local subject body smtp_dummy_pid smtp_dummy_port
+    test_subtest_broken_for_installed
     subject="$1"
     body="$2"
     shift 2
@@ -144,6 +145,13 @@ emacs_generate_script () {
        # Construct a little test script here for the benefit of the user,
        # (who can easily run "run_emacs" to get the same emacs environment
        # for investigating any failures).
+    if [ -z "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       find_notmuch_el='--directory "$NOTMUCH_BUILDDIR/emacs"'
+    else
+       ### XXX FIXME: this should really use the installed emacs lisp files
+       find_notmuch_el='--directory "$NOTMUCH_SRCDIR/emacs"'
+    fi
+
        cat <<EOF >"$TMP_DIRECTORY/run_emacs"
 #!/bin/sh
 export PATH=$PATH
@@ -158,8 +166,8 @@ export NOTMUCH_CONFIG=$NOTMUCH_CONFIG
 #
 # --load               Force loading of notmuch.el and test-lib.el
 
-exec ${TEST_EMACS} --quick \
-       --directory "$NOTMUCH_BUILDDIR/emacs" --load notmuch.el \
+exec ${TEST_EMACS} ${find_notmuch_el} --quick \
+       ${EXTRA_DIR} --load notmuch.el \
        --directory "$NOTMUCH_SRCDIR/test" --load test-lib.el \
        "\$@"
 EOF
index 79a9d4d6fc5b574cbd21e6e9bd27039689a2d01a..4cfb8ef163aac196558c81adae112091661036ff 100644 (file)
 
 ;;; Code:
 
+;; minimize impact of native compilation on the test suite.
+;; These are the Emacs 29.1 version of the variables.
+;; Leave trampolines enabled per Emacs upstream recommendations.
+;; It is important to set these variables before loading any
+;; .elc files.
+(setq native-comp-jit-compilation nil)
+(setq native-comp-speed -1)
+(setq native-comp-async-jobs-number 1)
+
 (require 'cl-lib)
 
 ;; Ensure that the dynamic variables that are defined by this library
@@ -205,3 +214,8 @@ running, quit if it terminated."
 ;; environments
 
 (setq mm-text-html-renderer 'html2text)
+
+;; Set our own default for message-hidden-headers, to avoid tests
+;; breaking when the Emacs default changes.
+(setq message-hidden-headers
+      '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:"))
index eec5c5b4508c1224d7b2510fe3822ce13e374bc0..059e110c87d540bb5bdd8baca0ef6e2140a1bfc4 100644 (file)
@@ -29,8 +29,8 @@ shopt -u xpg_echo
 # Ensure NOTMUCH_SRCDIR and NOTMUCH_BUILDDIR are set.
 . $(dirname "$0")/export-dirs.sh || exit 1
 
-# It appears that people try to run tests without building...
-if [[ ! -x "$NOTMUCH_BUILDDIR/notmuch" ]]; then
+# We need either a built tree, or a promise of an installed notmuch
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" -a ! -x "$NOTMUCH_BUILDDIR/notmuch" ]; then
        echo >&2 'You do not seem to have built notmuch yet.'
        exit 1
 fi
@@ -388,6 +388,14 @@ test_expect_equal_message_body () {
     test "$#" = 2 ||
        error "bug in the test script: not 2 parameters to test_expect_equal_file"
 
+    for file in "$1" "$2"; do
+       if [ ! -s "$file" ]; then
+           test_failure_ "Missing or zero length file: $file"
+           inside_subtest=
+           return 1
+       fi
+    done
+
     expected=$(sed '1,/^$/d' "$1")
     output=$(sed '1,/^$/d' "$2")
     test_expect_equal "$expected" "$output"
@@ -740,6 +748,18 @@ test_subtest_known_broken () {
        test_subtest_known_broken_=t
 }
 
+test_subtest_broken_for_installed () {
+    if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       test_subtest_known_broken_=t
+    fi
+}
+
+test_subtest_broken_for_root () {
+   if [ "$EUID" = "0" ]; then
+       test_subtest_known_broken_=t
+    fi
+}
+
 test_expect_success () {
        exec 1>&6 2>&7          # Restore stdout and stderr
        if [ -z "$inside_subtest" ]; then
@@ -915,11 +935,16 @@ make_shim () {
 }
 
 notmuch_with_shim () {
-    local base_name shim_file
-    base_name="$1"
+    local base_name shim_file notmuch_cmd
+    if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       notmuch_cmd="notmuch"
+    else
+       notmuch_cmd="notmuch-shared"
+    fi
+    base_name=$1
     shift
     shim_file="${base_name}.so"
-    LD_PRELOAD=${LD_PRELOAD:+:$LD_PRELOAD}:./${shim_file} notmuch-shared "$@"
+    LD_PRELOAD=${LD_PRELOAD:+:$LD_PRELOAD}:./${shim_file} $notmuch_cmd "$@"
 }
 
 # Creates a script that counts how much time it is executed and calls
@@ -971,7 +996,11 @@ test_init_ () {
 
 
 # Where to run the tests
-TEST_DIRECTORY=$NOTMUCH_BUILDDIR/test
+if [[ -n "${NOTMUCH_BUILDDIR}" ]]; then
+    TEST_DIRECTORY=$NOTMUCH_BUILDDIR/test
+else
+    TEST_DIRECTORY=$NOTMUCH_SRCDIR/test
+fi
 
 . "$NOTMUCH_SRCDIR/test/test-lib-common.sh" || exit 1
 
diff --git a/util/xapian-extra.h b/util/xapian-extra.h
new file mode 100644 (file)
index 0000000..39c7f48
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _XAPIAN_EXTRA_H
+#define _XAPIAN_EXTRA_H
+
+#include <string>
+#include <xapian.h>
+
+inline Xapian::Query
+xapian_query_match_all (void)
+{
+    // Xapian::Query::MatchAll isn't thread safe (a static object with reference
+    // counting) so instead reconstruct the equivalent on demand.
+    return Xapian::Query (std::string ());
+}
+
+#endif
index c128d4d9e49568bf12a01322b7981ebbfdb2c058..f2687f32e8878bb36f76e6cb14a4b09169e25f8b 100644 (file)
@@ -1 +1 @@
-0.37
+0.38.3
index c137bacdc0f4f1e3af78cb9617d5fe92f527c53f..777c20c0e6f0b8a695fe2d997ddb7284660e5de2 100644 (file)
@@ -53,10 +53,10 @@ Enjoy ;)
 As an example to configure a key mapping to add the tag 'to-do' and archive,
 this is what I use:
 
-let g:notmuch_rb_custom_search_maps = {
+let g:notmuch_custom_search_maps = {
        \ 't':          'search_tag("+to-do -inbox")',
        \ }
 
-let g:notmuch_rb_custom_show_maps = {
+let g:notmuch_custom_show_maps = {
        \ 't':          'show_tag("+to-do -inbox")',
        \ }
index 43741022e13580cafd7f1190a601608b14afcafe..c98f2b53d80df5c50fa13142a73ebc53b50e5075 100644 (file)
@@ -89,7 +89,7 @@ s     Send
 CONFIGURATION                                  *notmuch-config*
 
 You can add the following configurations to your `.vimrc`, or
-`~/.vim/plugin/notmuch.vim`.
+`~/.vim/after/plugin/notmuch.vim`.
 
                                                *g:notmuch_folders*
 
@@ -138,13 +138,13 @@ You can do the same for the thread view:
 
 If you want to count the threads instead of the messages in the folder view:
 >
-       let g:notmuch_folders_count_threads = 0
+       let g:notmuch_folders_count_threads = 1
 <
 
                                                *g:notmuch_reader*
                                                *g:notmuch_sendmail*
 
-You can also configure your externail mail reader and sendemail program:
+You can also configure your external mail reader and sendmail program:
 >
        let g:notmuch_reader = 'mutt -f %s'
        let g:notmuch_sendmail = 'sendmail'