summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJameson Graef Rollins <jrollins@finestructure.net>2009-12-05 01:19:53 -0500
committerJameson Graef Rollins <jrollins@finestructure.net>2009-12-05 01:20:11 -0500
commite72a6176e3fc3fcf4b1696e3f0ee9cf66509fb4d (patch)
tree7015964358d20934cbc78a50340870d9b81fcd1f
parent4edf37a1d5067197741b8dcb0bdb72ce4c299c05 (diff)
parent263aeb82f0a1a69864ca41cff4dfa2ae0b8b3f05 (diff)
merge changes from upstream
-rw-r--r--.gitignore1
-rw-r--r--Makefile60
-rw-r--r--Makefile.local15
-rw-r--r--TODO31
-rw-r--r--compat/Makefile5
-rw-r--r--compat/Makefile.local8
-rw-r--r--compat/compat.h41
-rw-r--r--compat/getdelim.c133
-rw-r--r--compat/getline.c29
-rw-r--r--config/README5
-rw-r--r--config/have_getline.c13
-rwxr-xr-xconfigure229
-rw-r--r--lib/index.cc10
-rw-r--r--lib/notmuch-private.h14
-rw-r--r--lib/xutil.c8
-rw-r--r--notmuch-client.h3
-rw-r--r--notmuch-config.c2
-rw-r--r--notmuch-new.c4
-rw-r--r--notmuch-reply.c6
-rw-r--r--notmuch-setup.c15
-rw-r--r--notmuch-show.c37
-rw-r--r--notmuch-tag.c4
-rw-r--r--notmuch.118
-rw-r--r--notmuch.c9
-rw-r--r--notmuch.el604
-rw-r--r--vim/plugin/notmuch.vim9
26 files changed, 957 insertions, 356 deletions
diff --git a/.gitignore b/.gitignore
index 8794354..efa98fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+Makefile.config
TAGS
tags
*cscope*
diff --git a/Makefile b/Makefile
index 2cd1b1b..021fdb8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,5 @@
-# Default FLAGS, (can be overridden by user such as "make CFLAGS=-O2")
-WARN_FLAGS=-Wall -Wextra -Wmissing-declarations -Wwrite-strings -Wswitch-enum
-CFLAGS=-O2
+WARN_CXXFLAGS=-Wall -Wextra -Wwrite-strings -Wswitch-enum
+WARN_CFLAGS=$(WARN_CXXFLAGS) -Wmissing-declarations
# Additional programs that are used during the compilation process.
EMACS ?= emacs
@@ -8,66 +7,69 @@ EMACS ?= emacs
# arguments to gzip.
gzip = gzip
-# Additional flags that we will append to whatever the user set.
-# These aren't intended for the user to manipulate.
-extra_cflags := $(shell pkg-config --cflags glib-2.0 gmime-2.4 talloc)
-extra_cxxflags := $(shell xapian-config --cxxflags)
-
-emacs_lispdir := $(shell pkg-config emacs --variable sitepkglispdir)
-# Hard-code if this system doesn't have an emacs.pc file
-ifeq ($(emacs_lispdir),)
- emacs_lispdir = $(prefix)/share/emacs/site-lisp
-endif
+bash_completion_dir = /etc/bash_completion.d
all_deps = Makefile Makefile.local Makefile.config \
lib/Makefile lib/Makefile.local
+extra_cflags :=
+extra_cxxflags :=
+
# Now smash together user's values with our extra values
-override CFLAGS += $(WARN_FLAGS) $(extra_cflags)
-override CXXFLAGS += $(WARN_FLAGS) $(extra_cflags) $(extra_cxxflags)
+FINAL_CFLAGS = $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags)
+FINAL_CXXFLAGS = $(CXXFLAGS) $(WARN_CXXFLAGS) $(CONFIGURE_CXXFLAGS) $(extra_cflags) $(extra_cxxflags)
+FINAL_LDFLAGS = $(LDFLAGS) $(CONFIGURE_LDFLAGS)
+
+all: notmuch notmuch.1.gz
+
+# Before including any other Makefile fragments, get settings from the
+# output of configure
+Makefile.config: configure
+ @echo ""
+ @echo "Note: Calling ./configure with no command-line arguments. This is often fine,"
+ @echo " but if you want to specify any arguments (such as an alternate prefix"
+ @echo " into which to install), call ./configure explicitly and then make again."
+ @echo " See \"./configure --help\" for more details."
+ @echo ""
+ ./configure
-override LDFLAGS += \
- $(shell pkg-config --libs glib-2.0 gmime-2.4 talloc) \
- $(shell xapian-config --libs)
+include Makefile.config
-# Include our local Makefile.local first so that its first target is default
-include Makefile.local
include lib/Makefile.local
-
-# And get user settings from the output of configure
-include Makefile.config
+include compat/Makefile.local
+include Makefile.local
# The user has not set any verbosity, default to quiet mode and inform the
# user how to enable verbose compiles.
ifeq ($(V),)
quiet_DOC := "Use \"$(MAKE) V=1\" to see the verbose compile lines.\n"
-quiet = @echo $(quiet_DOC)$(eval quiet_DOC:=)" $1 $@"; $($1)
+quiet = @printf $(quiet_DOC)$(eval quiet_DOC:=)" $1 $2 $@\n"; $($1)
endif
# The user has explicitly enabled quiet compilation.
ifeq ($(V),0)
-quiet = @echo " $1 $@"; $($1)
+quiet = @printf " $1 $@\n"; $($1)
endif
# Otherwise, print the full command line.
quiet ?= $($1)
%.o: %.cc $(all_deps)
- $(call quiet,CXX) -c $(CXXFLAGS) $< -o $@
+ $(call quiet,CXX,$(CXXFLAGS)) -c $(FINAL_CXXFLAGS) $< -o $@
%.o: %.c $(all_deps)
- $(call quiet,CC) -c $(CFLAGS) $< -o $@
+ $(call quiet,CC,$(CFLAGS)) -c $(FINAL_CFLAGS) $< -o $@
%.elc: %.el
$(call quiet,EMACS) -batch -f batch-byte-compile $<
.deps/%.d: %.c $(all_deps)
@set -e; rm -f $@; mkdir -p $$(dirname $@) ; \
- $(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@.$$$$; \
+ $(CC) -M $(CPPFLAGS) $(FINAL_CFLAGS) $< > $@.$$$$ 2>/dev/null ; \
sed 's,'$$(basename $*)'\.o[ :]*,$*.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
.deps/%.d: %.cc $(all_deps)
@set -e; rm -f $@; mkdir -p $$(dirname $@) ; \
- $(CXX) -M $(CPPFLAGS) $(CXXFLAGS) $< > $@.$$$$; \
+ $(CXX) -M $(CPPFLAGS) $(FINAL_CXXFLAGS) $< > $@.$$$$ 2>/dev/null ; \
sed 's,'$$(basename $*)'\.o[ :]*,$*.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
diff --git a/Makefile.local b/Makefile.local
index b818627..933ff4c 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -1,8 +1,7 @@
-all: notmuch notmuch.1.gz
-
emacs: notmuch.elc
notmuch_client_srcs = \
+ $(notmuch_compat_srcs) \
debugger.c \
gmime-filter-reply.c \
notmuch.c \
@@ -23,21 +22,18 @@ notmuch_client_srcs = \
notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
notmuch: $(notmuch_client_modules) lib/notmuch.a
- $(call quiet,CXX) $^ $(LDFLAGS) -o $@
+ $(call quiet,CXX,$(LDFLAGS)) $^ $(FINAL_LDFLAGS) -o $@
notmuch.1.gz: notmuch.1
$(call quiet,gzip) --stdout $^ > $@
install: all notmuch.1.gz
- for d in $(DESTDIR)$(prefix)/bin/ $(DESTDIR)$(prefix)/share/man/man1 \
- $(DESTDIR)$(bash_completion_dir) ; \
+ for d in $(DESTDIR)$(prefix)/bin/ $(DESTDIR)$(prefix)/share/man/man1 ; \
do \
install -d $$d ; \
done ;
install notmuch $(DESTDIR)$(prefix)/bin/
install -m0644 notmuch.1.gz $(DESTDIR)$(prefix)/share/man/man1/
- install -m0644 contrib/notmuch-completion.bash \
- $(DESTDIR)$(bash_completion_dir)/notmuch
install-emacs: install emacs
for d in $(DESTDIR)/$(emacs_lispdir) ; \
@@ -47,5 +43,10 @@ install-emacs: install emacs
install -m0644 notmuch.el $(DESTDIR)$(emacs_lispdir)
install -m0644 notmuch.elc $(DESTDIR)$(emacs_lispdir)
+install-bash:
+ install -d $(DESTDIR)$(bash_completion_dir)
+ install -m0644 contrib/notmuch-completion.bash \
+ $(DESTDIR)$(bash_completion_dir)/notmuch
+
SRCS := $(SRCS) $(notmuch_client_srcs)
CLEAN := $(CLEAN) notmuch $(notmuch_client_modules) notmuch.elc notmuch.1.gz
diff --git a/TODO b/TODO
index 1b8fb42..d6c303e 100644
--- a/TODO
+++ b/TODO
@@ -8,9 +8,6 @@ Fix the things that are causing the most pain to new users
Emacs interface (notmuch.el)
----------------------------
-Make the keybindings help ('?') display the summary of each command's
-documentation, not the function name.
-
Add a global keybinding table for notmuch, and then view-specific
tables that add to it.
@@ -18,8 +15,6 @@ Add a command to archive all threads in a search view.
Add a '|' binding from the search view.
-Add a binding to run a search from notmuch-show-mode.
-
When a thread has been entirely read, start out by closing all
messages except those that matched the search terms.
@@ -41,10 +36,6 @@ Portability
-----------
Fix configure script to test each compiler warning we want to use.
-Implement strndup locally (or call talloc_strndup instead).
-
-Implement getline locally, (look at gnulib).
-
Completion
----------
Fix bash completion to complete multiple search options (both --first
@@ -53,6 +44,11 @@ and *then* --max-threads), and also complete value for --sort=
notmuch command-line tool
-------------------------
+Fix "notmuch show" so that the UI doesn't fail to show a thread that
+is visible in a search buffer, but happens to no longer match the
+current search. (Perhaps add a --matching=<secondary-search-terms>
+option (or similar) to "notmuch show".)
+
Teach "notmuch search" to return many different kinds of results. Some
ideas:
@@ -72,6 +68,10 @@ Give "notmuch restore" some progress indicator. Until we get the
Xapian bugs fixed that are making this operation slow, we really need
to let the user know that things are still moving.
+Fix "notmuch restore" to operate in a single pass much like "notmuch
+dump" does, rather than doing N searches into the database, each
+matching 1/N messages.
+
Add a "-f <filename>" option to select an alternate configuration
file.
@@ -80,10 +80,6 @@ relative to the database path. (Otherwise, moving the database to a
new directory will result in notmuch creating new timestamp documents
and leaving stale ones behind.)
-Ensure that "notmuch new" is sane if its first, giant indexing session
-gets interrupted, (that is, ensure that any results indexed so far are
-flushed).
-
Fix notmuch.c to use a DIR prefix for directory timestamps, (the idea
being that it can then add other non-directory timestamps such as for
noting how far back in the past mail has been indexed, and whether it
@@ -104,10 +100,12 @@ indexing.
notmuch library
---------------
+Index content from citations, please.
+
Provide a sane syntax for date ranges. First, we don't want to require
both endpoints to be specified. For example it would be nice to be
able to say things like "since:2009-01-1" or "until:2009-01-1" and
-have the other enpoint be implicit. Second we'de like to support
+have the other endpoint be implicit. Second we'd like to support
relative specifications of time such as "since:'2 months ago'". To do
any of this we're probably going to need to break down an write our
own parser for the query string rather than using Xapian's QueryParser
@@ -132,11 +130,6 @@ Add support for configuring "virtual tags" which are a tuple of
(tag-name, search-specification). The database is responsible for
ensuring that the virtual tag is always consistent.
-Think about optimizing chunked searches (max-threads > 0) to avoid
-repeating work. That would be saving state from the previous chunk and
-reusing it if the next search is the next chunk with the same search
-string.
-
General
-------
Audit everything for dealing with out-of-memory (and drop xutil.c).
diff --git a/compat/Makefile b/compat/Makefile
new file mode 100644
index 0000000..9a29ffc
--- /dev/null
+++ b/compat/Makefile
@@ -0,0 +1,5 @@
+all:
+ $(MAKE) -C .. all
+
+clean:
+ $(MAKE) -C .. clean
diff --git a/compat/Makefile.local b/compat/Makefile.local
new file mode 100644
index 0000000..ccc59ae
--- /dev/null
+++ b/compat/Makefile.local
@@ -0,0 +1,8 @@
+dir=compat
+extra_cflags += -I$(dir)
+
+notmuch_compat_srcs =
+
+ifneq ($(HAVE_GETLINE),1)
+notmuch_compat_srcs += $(dir)/getline.c $(dir)/getdelim.c
+endif
diff --git a/compat/compat.h b/compat/compat.h
new file mode 100644
index 0000000..d639e0f
--- /dev/null
+++ b/compat/compat.h
@@ -0,0 +1,41 @@
+/* notmuch - Not much of an email library, (just index and search)
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * 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 http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+/* This header file defines functions that will only be conditionally
+ * compiled for compatibility on systems that don't provide their own
+ * implementations of the functions.
+ */
+
+#ifndef NOTMUCH_COMPAT_H
+#define NOTMUCH_COMPAT_H
+
+#if !HAVE_GETLINE
+#include <stdio.h>
+#include <unistd.h>
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream);
+
+ssize_t
+getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp);
+
+#endif /* !HAVE_GETLINE */
+
+#endif /* NOTMUCH_COMPAT_H */
diff --git a/compat/getdelim.c b/compat/getdelim.c
new file mode 100644
index 0000000..407f3d0
--- /dev/null
+++ b/compat/getdelim.c
@@ -0,0 +1,133 @@
+/* getdelim.c --- Implementation of replacement getdelim function.
+ Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005, 2006, 2007,
+ 2008, 2009 Free Software Foundation, Inc.
+
+ 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, 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Ported from glibc by Simon Josefsson. */
+
+#include "compat.h"
+
+#include <stdio.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#endif
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+# define getc_maybe_unlocked(fp) getc(fp)
+#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED
+# undef flockfile
+# undef funlockfile
+# define flockfile(x) ((void) 0)
+# define funlockfile(x) ((void) 0)
+# define getc_maybe_unlocked(fp) getc(fp)
+#else
+# define getc_maybe_unlocked(fp) getc_unlocked(fp)
+#endif
+
+/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
+ NUL-terminate it). *LINEPTR is a pointer returned from malloc (or
+ NULL), pointing to *N characters of space. It is realloc'ed as
+ necessary. Returns the number of characters read (not including
+ the null terminator), or -1 on error or EOF. */
+
+ssize_t
+getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
+{
+ ssize_t result = -1;
+ size_t cur_len = 0;
+
+ if (lineptr == NULL || n == NULL || fp == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ flockfile (fp);
+
+ if (*lineptr == NULL || *n == 0)
+ {
+ char *new_lineptr;
+ *n = 120;
+ new_lineptr = (char *) realloc (*lineptr, *n);
+ if (new_lineptr == NULL)
+ {
+ result = -1;
+ goto unlock_return;
+ }
+ *lineptr = new_lineptr;
+ }
+
+ for (;;)
+ {
+ int i;
+
+ i = getc_maybe_unlocked (fp);
+ if (i == EOF)
+ {
+ result = -1;
+ break;
+ }
+
+ /* Make enough space for len+1 (for final NUL) bytes. */
+ if (cur_len + 1 >= *n)
+ {
+ size_t needed_max =
+ SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
+ size_t needed = 2 * *n + 1; /* Be generous. */
+ char *new_lineptr;
+
+ if (needed_max < needed)
+ needed = needed_max;
+ if (cur_len + 1 >= needed)
+ {
+ result = -1;
+ errno = EOVERFLOW;
+ goto unlock_return;
+ }
+
+ new_lineptr = (char *) realloc (*lineptr, needed);
+ if (new_lineptr == NULL)
+ {
+ result = -1;
+ goto unlock_return;
+ }
+
+ *lineptr = new_lineptr;
+ *n = needed;
+ }
+
+ (*lineptr)[cur_len] = i;
+ cur_len++;
+
+ if (i == delimiter)
+ break;
+ }
+ (*lineptr)[cur_len] = '\0';
+ result = cur_len ? (ssize_t) cur_len : result;
+
+ unlock_return:
+ funlockfile (fp); /* doesn't set errno */
+
+ return result;
+}
diff --git a/compat/getline.c b/compat/getline.c
new file mode 100644
index 0000000..222e0f6
--- /dev/null
+++ b/compat/getline.c
@@ -0,0 +1,29 @@
+/* getline.c --- Implementation of replacement getline function.
+ Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+
+ 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, 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Written by Simon Josefsson. */
+
+#include "compat.h"
+
+#include <stdio.h>
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+ return getdelim (lineptr, n, '\n', stream);
+}
diff --git a/config/README b/config/README
new file mode 100644
index 0000000..eabfe28
--- /dev/null
+++ b/config/README
@@ -0,0 +1,5 @@
+notmuch/config
+
+This directory consists of small programs used by the notmuch
+configure script to test for the availability of certain system
+features, (library functions, etc.).
diff --git a/config/have_getline.c b/config/have_getline.c
new file mode 100644
index 0000000..a8bcd17
--- /dev/null
+++ b/config/have_getline.c
@@ -0,0 +1,13 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/types.h>
+
+int main()
+{
+ ssize_t count = 0;
+ size_t n = 0;
+ char **lineptr = NULL;
+ FILE *stream = NULL;
+
+ count = getline(lineptr, &n, stream);
+}
diff --git a/configure b/configure
index 1010799..fa8e142 100755
--- a/configure
+++ b/configure
@@ -1,12 +1,70 @@
#! /bin/sh
-# defaults
+# Set several defaults (optionally specified by the user in
+# environemnt variables)
+CC=${CC:-gcc}
+CXX=${CXX:-g++}
+CFLAGS=${CFLAGS:--O2}
+CXXFLAGS=${CXXFLAGS:-\$(CFLAGS)}
+
+# Set the defaults for values the user can specify with command-line
+# options.
PREFIX=/usr/local
-# option parsing
+usage ()
+{
+ cat <<EOF
+Usage: ./configure [options]...
+
+This script configures notmuch to build on your system.
+
+It verifies that dependencies are available, determines flags needed
+to compile and link against various required libraries, and identifies
+whether various system functions can be used or if locally-provided
+replacements will be built instead.
+
+Finally, it allows you to control various aspects of the build and
+installation process.
+
+First, some common variables can specified via environment variables:
+
+ CC The C compiler to use
+ CFLAGS Flags to pass to the C compiler
+ CXX The C++ compiler to use
+ CXXFLAGS Flags to pass to the C compiler
+ LDFLAGS Flags to pass when linking
+
+Each of these values can further be controlled by specifying them
+later on the "make" command line.
+
+Additionally, various options can be specified on the configure
+command line.
+
+ --prefix=PREFIX Install files in PREFIX [$PREFIX]
+
+By default, "make install" will install the resulting program to
+$PREFIX/bin, documentation to $PREFIX/share, etc. You can
+specify an installation prefix other than $PREFIX using
+--prefix, for instance:
+
+ ./configure --prefix=\$HOME
+
+EOF
+}
+
+# Parse command-line options
for option; do
- if [ "${option%=*}" = '--prefix' ] ; then
+ if [ "${option}" = '--help' ] ; then
+ usage
+ exit 0
+ elif [ "${option%%=*}" = '--prefix' ] ; then
PREFIX="${option#*=}"
+ else
+ echo "Unrecognized option: ${option}."
+ echo "See:"
+ echo " $0 --help"
+ echo ""
+ exit 1
fi
done
@@ -17,14 +75,16 @@ We hope that the process of building and installing notmuch is quick
and smooth so that you can soon be reading and processing your email
more efficiently than ever.
-If anything goes wrong in this process, please do as much as you can
-to figure out what could be different on your machine compared to
-those of the notmuch developers. Then, please email those details to
-the Notmuch list (notmuch@notmuchmail.org) so that we can hopefully make
-future versions of notmuch easier for you to use.
+If anything goes wrong in the configure process, you can override any
+decisions it makes by manually editing the Makefile.config file that
+it creates. Also please do as much as you can to figure out what could
+be different on your machine compared to those of the notmuch
+developers. Then, please email those details to the Notmuch list
+(notmuch@notmuchmail.org) so that we can hopefully make future
+versions of notmuch easier for you to use.
-We'll now investigate your system to find verify that various software
-components that notmuch relies on are available.
+We'll now investigate your system to verify that all required
+dependencies are available:
EOF
@@ -36,48 +96,57 @@ else
have_pkg_config=0
fi
+printf "Checking for Xapian development files... "
if xapian-config --version > /dev/null 2>&1; then
- echo "Checking for Xapian development files... Yes."
+ printf "Yes.\n"
have_xapian=1
+ xapian_cxxflags=$(xapian-config --cxxflags)
+ xapian_ldflags=$(xapian-config --libs)
else
- echo "Checking for Xapian development files... No."
+ printf "No.\n"
have_xapian=0
errors=$((errors + 1))
fi
+printf "Checking for GMime 2.4 development files... "
if pkg-config --modversion gmime-2.4 > /dev/null 2>&1; then
- echo "Checking for GMime 2.4 development files... Yes."
+ printf "Yes.\n"
have_gmime=1
+ gmime_cflags=$(pkg-config --cflags gmime-2.4)
+ gmime_ldflags=$(pkg-config --libs gmime-2.4)
else
- echo "Checking for GMime 2.4 development files... No."
+ printf "No.\n"
have_gmime=0
errors=$((errors + 1))
fi
+printf "Checking for talloc development files... "
if pkg-config --modversion talloc > /dev/null 2>&1; then
- echo "Checking for talloc development files... Yes."
+ printf "Yes.\n"
have_talloc=1
+ talloc_cflags=$(pkg-config --cflags talloc)
+ talloc_ldflags=$(pkg-config --libs talloc)
else
- echo "Checking for talloc development files... No."
+ printf "No.\n"
have_talloc=0
+ talloc_cflags=
errors=$((errors + 1))
fi
-if printf 'int main(){return 0;}' | gcc -x c -lz -o /dev/null - > /dev/null 2>&1; then
- echo "Checking for zlib development files... Yes."
- have_zlib=1
+printf "Checking for valgrind development files... "
+if pkg-config --modversion valgrind > /dev/null 2>&1; then
+ printf "Yes.\n"
+ have_valgrind=1
+ valgrind_cflags=$(pkg-config --cflags valgrind)
else
- echo "Checking for zlib development files... No."
- have_zlib=0
- errors=$((errors + 1))
+ printf "No (but that's fine).\n"
+ have_valgrind=0
fi
-if pkg-config --modversion valgrind > /dev/null 2>&1; then
- echo "Checking for valgrind development files... Yes."
- have_valgrind=-DHAVE_VALGRIND
+if pkg-config --modversion emacs > /dev/null 2>&1; then
+ emacs_lispdir=$(pkg-config emacs --variable sitepkglispdir)
else
- echo "Checking for valgrind development files... No."
- have_valgrind=
+ emacs_lispdir='$(prefix)/share/emacs/site-lisp'
fi
if [ $errors -gt 0 ]; then
@@ -100,34 +169,39 @@ EOF
echo " The talloc library (including development files such as headers)"
echo " http://talloc.samba.org/"
fi
- if [ $have_zlib -eq 0 ]; then
- echo " The zlib library (including development files such as headers)"
- fi
cat <<EOF
-On a modern, package-based operating system such as Debian, you can
-install all of the dependencies with the following simple command
-line:
+With any luck, you're using a modern, package-based operating system
+that has all of these packages available in the distribution. In that
+case a simple command will install everything you need. For example:
- sudo apt-get install libxapian-dev libgmime-2.4-dev libtalloc-dev libz-dev
+On Debian and similar systems:
-On other systems, a similar command can be used, but the details of the
-package names may be different, (such as "devel" in place of "dev").
+ sudo apt-get install libxapian-dev libgmime-2.4-dev libtalloc-dev
+
+Or on Fedora and similar systems:
+
+ sudo yum install xapian-core-devel gmime-devel libtalloc-devel
+
+On other systems, similar commands can be used, but the details of the
+package names may be different.
EOF
if [ $have_pkg_config -eq 0 ]; then
cat <<EOF
-Note: the pkg-config program is not available. Both this configure
-script and the Makefile of notmuch use pkg-config to find the
-compilation flags required to link against the various libraries
-needed by notmuch. It's possible you simply need to install pkg-config
-with a command such as:
+Note: the pkg-config program is not available. This configure script
+uses pkg-config to find the compilation flags required to link against
+the various libraries needed by notmuch. It's possible you simply need
+to install pkg-config with a command such as:
sudo apt-get install pkg-config
+Or:
+ sudo yum install pkgconfig
But if pkg-config is not available for your system, then you will need
-to manually edit the notmuch Makefile to set NOTMUCH_CFLAGS and
-NOTMUCH_LDFLAGS to the correct values without calling pkg-config.
+to modify the configure script to manually set the cflags and ldflags
+variables to the correct values to link against each library in each
+case that pkg-config could not be used to determine those values.
EOF
fi
@@ -140,6 +214,17 @@ EOF
exit 1
fi
+printf "Checking for getline... "
+if ${CC} -o config/have_getline config/have_getline.c > /dev/null 2>&1
+then
+ printf "Yes.\n"
+ have_getline=1
+else
+ printf "No (will use our own instead).\n"
+ have_getline=0
+fi
+rm -f config/have_getline
+
cat <<EOF
All required packages were found. You may now run the following
@@ -152,7 +237,59 @@ EOF
# construct the Makefile.config
cat > Makefile.config <<EOF
-prefix = $PREFIX
-bash_completion_dir = /etc/bash_completion.d
-CFLAGS += ${have_valgrind}
+# This Makefile.config was automatically generated by the ./configure
+# script of notmuch. If the configure script identified anything
+# incorrectly, then you can edit this file to try to correct things,
+# but be warned that if configure is run again it will destroy your
+# changes, (and this could happen by simply calling "make" if the
+# configure script is updated).
+
+# The C compiler to use
+CC = ${CC}
+
+# The C++ compiler to use
+CXX = ${CXX}
+
+# Default FLAGS for C compiler (can be overridden by user such as "make CFLAGS=-g")
+CFLAGS = ${CFLAGS}
+
+# Default FLAGS for C++ compiler (can be overridden by user such as "make CXXFLAGS=-g")
+CXXFLAGS = ${CXXFLAGS}
+
+# The prefix to which notmuch should be installed
+prefix = ${PREFIX}
+
+# The directory to which emacs lisp files should be installed
+emacs_lispdir=${emacs_lispdir}
+
+# Whether the getline function is available (if not, then notmuch will
+# build its own version)
+HAVE_GETLINE = ${have_getline}
+
+# Flags needed to compile and link against Xapian
+XAPIAN_CXXFLAGS = ${xapian_cxxflags}
+XAPIAN_LDFLAGS = ${xapian_ldflags}
+
+# Flags needed to compile and link against GMime-2.4
+GMIME_CFLAGS = ${gmime_cflags}
+GMIME_LDFLAGS = ${gmime_ldflags}
+
+# Flags needed to compile and link against talloc
+TALLOC_CFLAGS = ${talloc_cflags}
+TALLOC_LDFLAGS = ${talloc_ldflags}
+
+# Whether valgrind header files are available
+HAVE_VALGRIND = ${have_valgrind}
+
+# And if so, flags needed at compile time for valgrind macros
+VALGRIND_CFLAGS = ${valgrind_cflags}
+
+# Combined flags for compiling and linking against all of the above
+CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
+ \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\
+ \$(VALGRIND_CFLAGS)
+CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
+ \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\
+ \$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS)
+CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(XAPIAN_LDFLAGS)
EOF
diff --git a/lib/index.cc b/lib/index.cc
index 80df64b..125fa6c 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -31,7 +31,7 @@ _index_address_mailbox (notmuch_message_t *message,
{
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);
const char *name, *addr;
- int own_name = 0;
+ void *local = talloc_new (NULL);
name = internet_address_get_name (address);
addr = internet_address_mailbox_get_addr (mailbox);
@@ -42,16 +42,16 @@ _index_address_mailbox (notmuch_message_t *message,
const char *at;
at = strchr (addr, '@');
- if (at) {
- name = strndup (addr, at - addr);
- own_name = 1;
- }
+ if (at)
+ name = talloc_strndup (local, addr, at - addr);
}
if (name)
_notmuch_message_gen_terms (message, prefix_name, name);
if (addr)
_notmuch_message_gen_terms (message, prefix_name, addr);
+
+ talloc_free (local);
}
static void
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index d3f9a4c..0c340a7 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -26,6 +26,8 @@
#endif
#include <stdio.h>
+#include "compat.h"
+
#include "notmuch.h"
NOTMUCH_BEGIN_DECLS
@@ -330,18 +332,6 @@ void
_notmuch_message_add_reply (notmuch_message_t *message,
notmuch_message_node_t *reply);
-/* date.c */
-
-/* Parse an RFC 8222 date string to a time_t value.
- *
- * The tz_offset argument can be used to also obtain the time-zone
- * offset, (but can be NULL if the call is not interested in that).
- *
- * Returns 0 on error.
- */
-time_t
-notmuch_parse_date (const char *str, int *tz_offset);
-
/* sha1.c */
char *
diff --git a/lib/xutil.c b/lib/xutil.c
index 6fa5eb0..268225b 100644
--- a/lib/xutil.c
+++ b/lib/xutil.c
@@ -18,7 +18,6 @@
* Author: Carl Worth <cworth@cworth.org>
*/
-#define _GNU_SOURCE /* For strndup */
#include "notmuch-private.h"
#include <stdio.h>
@@ -84,11 +83,16 @@ xstrndup (const char *s, size_t n)
{
char *ret;
- ret = strndup (s, n);
+ if (strlen (s) <= n)
+ n = strlen (s);
+
+ ret = malloc (n + 1);
if (ret == NULL) {
fprintf (stderr, "Out of memory.\n");
exit (1);
}
+ memcpy (ret, s, n);
+ ret[n] = '\0';
return ret;
}
diff --git a/notmuch-client.h b/notmuch-client.h
index 2888a6c..50a30fe 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -21,12 +21,13 @@
#ifndef NOTMUCH_CLIENT_H
#define NOTMUCH_CLIENT_H
-
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* for getline */
#endif
#include <stdio.h>
+#include "compat.h"
+
#include <gmime/gmime.h>
#include "notmuch.h"
diff --git a/notmuch-config.c b/notmuch-config.c
index fc65d6b..95430db 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -317,9 +317,11 @@ notmuch_config_save (notmuch_config_t *config)
fprintf (stderr, "Error saving configuration to %s: %s\n",
config->filename, error->message);
g_error_free (error);
+ g_free (data);
return 1;
}
+ g_free (data);
return 0;
}
diff --git a/notmuch-new.c b/notmuch-new.c
index f58a384..9d20616 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -35,8 +35,10 @@ static volatile sig_atomic_t interrupted;
static void
handle_sigint (unused (int sig))
{
+ ssize_t ignored;
static char msg[] = "Stopping... \n";
- write(2, msg, sizeof(msg)-1);
+
+ ignored = write(2, msg, sizeof(msg)-1);
interrupted = 1;
}
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 9ca1236..0cda72d 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -39,11 +39,17 @@ reply_part_content (GMimeObject *part)
{
GMimeStream *stream_stdout = NULL, *stream_filter = NULL;
GMimeDataWrapper *wrapper;
+ const char *charset;
+ charset = g_mime_object_get_content_type_parameter (part, "charset");
stream_stdout = g_mime_stream_file_new (stdout);
if (stream_stdout) {
g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE);
stream_filter = g_mime_stream_filter_new(stream_stdout);
+ if (charset) {
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
+ g_mime_filter_charset_new(charset, "UTF-8"));
+ }
}
g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
g_mime_filter_reply_new(TRUE));
diff --git a/notmuch-setup.c b/notmuch-setup.c
index 5ec176d..622bbaa 100644
--- a/notmuch-setup.c
+++ b/notmuch-setup.c
@@ -100,12 +100,15 @@ notmuch_setup_command (unused (void *ctx),
unsigned int i;
int is_new;
-#define prompt(format, ...) \
- do { \
- printf (format, ##__VA_ARGS__); \
- fflush (stdout); \
- getline (&response, &response_size, stdin); \
- chomp_newline (response); \
+#define prompt(format, ...) \
+ do { \
+ printf (format, ##__VA_ARGS__); \
+ fflush (stdout); \
+ if (getline (&response, &response_size, stdin) < 0) { \
+ printf ("Exiting.\n"); \
+ exit (1); \
+ } \
+ chomp_newline (response); \
} while (0)
config = notmuch_config_open (ctx, NULL, &is_new);
diff --git a/notmuch-show.c b/notmuch-show.c
index 13c91e4..376aacd 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -184,9 +184,12 @@ show_message (void *ctx, notmuch_message_t *message, int indent)
static void
-show_messages (void *ctx, notmuch_messages_t *messages, int indent)
+show_messages (void *ctx, notmuch_messages_t *messages, int indent,
+ notmuch_bool_t entire_thread)
{
notmuch_message_t *message;
+ notmuch_bool_t match;
+ int next_indent;
for (;
notmuch_messages_has_more (messages);
@@ -194,9 +197,17 @@ show_messages (void *ctx, notmuch_messages_t *messages, int indent)
{
message = notmuch_messages_get (messages);
- show_message (ctx, message, indent);
+ match = notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH);
- show_messages (ctx, notmuch_message_get_replies (message), indent + 1);
+ next_indent = indent;
+
+ if (match || entire_thread) {
+ show_message (ctx, message, indent);
+ next_indent = indent + 1;
+ }
+
+ show_messages (ctx, notmuch_message_get_replies (message),
+ next_indent, entire_thread);
notmuch_message_destroy (message);
}
@@ -212,6 +223,24 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
notmuch_thread_t *thread;
notmuch_messages_t *messages;
char *query_string;
+ int entire_thread = 0;
+ int i;
+
+ for (i = 0; i < argc && argv[i][0] == '-'; i++) {
+ if (strcmp (argv[i], "--") == 0) {
+ i++;
+ break;
+ }
+ if (strcmp(argv[i], "--entire-thread") == 0) {
+ entire_thread = 1;
+ } else {
+ fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ argc -= i;
+ argv += i;
config = notmuch_config_open (ctx, NULL, NULL);
if (config == NULL)
@@ -251,7 +280,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
INTERNAL_ERROR ("Thread %s has no toplevel messages.\n",
notmuch_thread_get_thread_id (thread));
- show_messages (ctx, messages, 0);
+ show_messages (ctx, messages, 0, entire_thread);
notmuch_thread_destroy (thread);
}
diff --git a/notmuch-tag.c b/notmuch-tag.c
index 07cb8c5..00588a1 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -25,8 +25,10 @@ static volatile sig_atomic_t interrupted;
static void
handle_sigint (unused (int sig))
{
+ ssize_t ignored;
+
static char msg[] = "Stopping... \n";
- write(2, msg, sizeof(msg)-1);
+ ignored = write(2, msg, sizeof(msg)-1);
interrupted = 1;
}
diff --git a/notmuch.1 b/notmuch.1
index 04bd0cf..369ecba 100644
--- a/notmuch.1
+++ b/notmuch.1
@@ -169,6 +169,8 @@ when sorting by
.B newest\-first
the threads will be sorted by the newest message in each thread.
+.RE
+.RS 4
By default, results will be displayed in reverse chronological order,
(that is, the newest results will be displayed first).
@@ -177,7 +179,7 @@ See the
section below for details of the supported syntax for <search-terms>.
.RE
.TP
-.BR show " <search-term>..."
+.BR show " [options...] <search-term>..."
Shows all messages matching the search terms.
@@ -187,6 +189,19 @@ message in date order). The output is not indented by default, but
depth tags are printed so that proper indentation can be performed by
a post-processor (such as the emacs interface to notmuch).
+Supported options for
+.B show
+include
+.RS 4
+.TP 4
+.B \-\-entire\-thread
+
+By default only those messages that match the search terms will be
+displayed. With this option, all messages in the same thread as any
+matched message will be displayed.
+.RE
+
+.RS 4
The output format is plain-text, with all text-content MIME parts
decoded. Various components in the output,
.RB ( message ", " header ", " body ", " attachment ", and MIME " part ),
@@ -207,6 +222,7 @@ See the
.B "SEARCH SYNTAX"
section below for details of the supported syntax for <search-terms>.
.RE
+.RE
The
.B reply
diff --git a/notmuch.c b/notmuch.c
index d9846ce..2ac8a59 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -177,6 +177,15 @@ command_t commands[] = {
"\t\t(all replies to a particular message appear immediately\n"
"\t\tafter that message in date order).\n"
"\n"
+ "\t\tSupported options for show include:\n"
+ "\n"
+ "\t\t--entire-thread\n"
+ "\n"
+ "\t\t\tBy default only those messages that match the\n"
+ "\t\t\tsearch terms will be displayed. With this option,\n"
+ "\t\t\tall messages in the same thread as any matched\n"
+ "\t\t\tmessage will be displayed.\n"
+ "\n"
"\t\tThe output format is plain-text, with all text-content\n"
"\t\tMIME parts decoded. Various components in the output,\n"
"\t\t('message', 'header', 'body', 'attachment', and MIME 'part')\n"
diff --git a/notmuch.el b/notmuch.el
index 65473ba..c504f46 100644
--- a/notmuch.el
+++ b/notmuch.el
@@ -53,37 +53,31 @@
(defvar notmuch-show-mode-map
(let ((map (make-sparse-keymap)))
- ; I don't actually want all of these toggle commands occupying
- ; keybindings. They steal valuable key-binding space, are hard
- ; to remember, and act globally rather than locally.
- ;
- ; Will be much preferable to switch to direct manipulation for
- ; toggling visibility of these components. Probably using
- ; overlays-at to query and manipulate the current overlay.
- (define-key map "a" 'notmuch-show-archive-thread)
- (define-key map "A" 'notmuch-show-mark-read-then-archive-thread)
- (define-key map "f" 'notmuch-show-forward-current)
- (define-key map "m" 'message-mail)
- (define-key map "n" 'notmuch-show-next-message)
- (define-key map "N" 'notmuch-show-mark-read-then-next-open-message)
- (define-key map "p" 'notmuch-show-previous-message)
- (define-key map (kbd "C-n") 'notmuch-show-next-line)
- (define-key map (kbd "C-p") 'notmuch-show-previous-line)
+ (define-key map "?" 'notmuch-help)
(define-key map "q" 'kill-this-buffer)
- (define-key map "r" 'notmuch-show-reply)
+ (define-key map (kbd "C-p") 'notmuch-show-previous-line)
+ (define-key map (kbd "C-n") 'notmuch-show-next-line)
+ (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
+ (define-key map (kbd "TAB") 'notmuch-show-next-button)
(define-key map "s" 'notmuch-search)
- (define-key map "v" 'notmuch-show-view-all-mime-parts)
- (define-key map "V" 'notmuch-show-view-raw-message)
+ (define-key map "m" 'message-mail)
+ (define-key map "f" 'notmuch-show-forward-current)
+ (define-key map "r" 'notmuch-show-reply)
+ (define-key map "|" 'notmuch-show-pipe-message)
(define-key map "w" 'notmuch-show-save-attachments)
- (define-key map "x" 'kill-this-buffer)
- (define-key map "+" 'notmuch-show-add-tag)
+ (define-key map "V" 'notmuch-show-view-raw-message)
+ (define-key map "v" 'notmuch-show-view-all-mime-parts)
(define-key map "-" 'notmuch-show-remove-tag)
+ (define-key map "+" 'notmuch-show-add-tag)
+ (define-key map "X" 'notmuch-show-mark-read-then-archive-then-exit)
+ (define-key map "x" 'notmuch-show-archive-thread-then-exit)
+ (define-key map "A" 'notmuch-show-mark-read-then-archive-thread)
+ (define-key map "a" 'notmuch-show-archive-thread)
+ (define-key map "p" 'notmuch-show-previous-message)
+ (define-key map "N" 'notmuch-show-mark-read-then-next-open-message)
+ (define-key map "n" 'notmuch-show-next-message)
(define-key map (kbd "DEL") 'notmuch-show-rewind)
(define-key map " " 'notmuch-show-advance-marking-read-and-archiving)
- (define-key map "|" 'notmuch-show-pipe-message)
- (define-key map "?" 'describe-mode)
- (define-key map (kbd "TAB") 'notmuch-show-next-button)
- (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
map)
"Keymap for \"notmuch show\" buffers.")
(fset 'notmuch-show-mode-map notmuch-show-mode-map)
@@ -117,7 +111,7 @@ pattern can still test against the entire line).")
(defvar notmuch-show-marker-regexp "\f\\(message\\|header\\|body\\|attachment\\|part\\)[{}].*$")
(defvar notmuch-show-id-regexp "\\(id:[^ ]*\\)")
-(defvar notmuch-show-depth-regexp " depth:\\([0-9]*\\) ")
+(defvar notmuch-show-depth-match-regexp " depth:\\([0-9]*\\).*match:\\([01]\\) ")
(defvar notmuch-show-filename-regexp "filename:\\(.*\\)$")
(defvar notmuch-show-tags-regexp "(\\([^)]*\\))$")
@@ -165,7 +159,7 @@ Unlike builtin `next-line' this version accepts no arguments."
By advancing forward until reaching a visible character.
-Unlike builtin `next-line' this version accepts no arguments."
+Unlike builtin `previous-line' this version accepts no arguments."
(interactive)
(set 'this-command 'previous-line)
(call-interactively 'previous-line)
@@ -252,7 +246,7 @@ Unlike builtin `next-line' this version accepts no arguments."
(notmuch-search-show-thread)))))
(defun notmuch-show-mark-read-then-archive-thread ()
- "Remove \"unread\" tag from each message, then archive and show next thread.
+ "Remove unread tags from thread, then archive and show next thread.
Archive each message currently shown by removing the \"unread\"
and \"inbox\" tag from each. Then kill this buffer and show the
@@ -267,7 +261,7 @@ buffer."
(notmuch-show-archive-thread-maybe-mark-read t))
(defun notmuch-show-archive-thread ()
- "Archive each message in thread, and show next thread from search.
+ "Archive each message in thread, then show next thread from search.
Archive each message currently shown by removing the \"inbox\"
tag from each. Then kill this buffer and show the next thread
@@ -280,6 +274,18 @@ buffer."
(interactive)
(notmuch-show-archive-thread-maybe-mark-read nil))
+(defun notmuch-show-archive-thread-then-exit ()
+ "Archive each message in thread, then exit back to search results."
+ (interactive)
+ (notmuch-show-archive-thread)
+ (kill-this-buffer))
+
+(defun notmuch-show-mark-read-then-archive-then-exit ()
+ "Remove unread tags from thread, then archive and exit to search results."
+ (interactive)
+ (notmuch-show-mark-read-then-archive-thread)
+ (kill-this-buffer))
+
(defun notmuch-show-view-raw-message ()
"View the raw email of the current message."
(interactive)
@@ -296,7 +302,7 @@ buffer."
(kill-buffer buf)))))
(defun notmuch-show-view-all-mime-parts ()
- "Use external viewers (according to mailcap) to view all MIME-encoded parts."
+ "Use external viewers to view all attachments from the current message."
(interactive)
(with-current-notmuch-show-message
(mm-display-parts (mm-dissect-buffer))))
@@ -334,7 +340,7 @@ buffer."
mm-handle))
(defun notmuch-show-save-attachments ()
- "Save the attachments to a message"
+ "Save all attachments from the current message."
(interactive)
(with-current-notmuch-show-message
(let ((mm-handle (mm-dissect-buffer)))
@@ -360,7 +366,7 @@ buffer."
(notmuch-reply message-id)))
(defun notmuch-show-forward-current ()
- "Forward a the current message."
+ "Forward the current message."
(interactive)
(with-current-notmuch-show-message
(message-forward)))
@@ -385,7 +391,7 @@ point either forward or backward to the next visible character
when a command ends with point on an invisible character).
Emits an error if point is not within a valid message, (that is
-not pattern of `notmuch-show-message-begin-regexp' could be found
+no pattern of `notmuch-show-message-begin-regexp' could be found
by searching backward)."
(beginning-of-line)
(if (not (looking-at notmuch-show-message-begin-regexp))
@@ -402,22 +408,35 @@ by searching backward)."
(not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
(defun notmuch-show-message-unread-p ()
- "Preficate testing whether current message is unread."
+ "Predicate testing whether current message is unread."
(member "unread" (notmuch-show-get-tags)))
+(defun notmuch-show-message-open-p ()
+ "Predicate testing whether current message is open (body is visible)."
+ (let ((btn (previous-button (point) t)))
+ (while (not (button-has-type-p btn 'notmuch-button-body-toggle-type))
+ (setq btn (previous-button (button-start btn))))
+ (not (invisible-p (button-get btn 'invisibility-spec)))))
+
(defun notmuch-show-next-message ()
"Advance to the beginning of the next message in the buffer.
Moves to the last visible character of the current message if
-already on the last message in the buffer."
+already on the last message in the buffer.
+
+Returns nil if already on the last message in the buffer."
(interactive)
(notmuch-show-move-to-current-message-summary-line)
(if (re-search-forward notmuch-show-message-begin-regexp nil t)
- (notmuch-show-move-to-current-message-summary-line)
+ (progn
+ (notmuch-show-move-to-current-message-summary-line)
+ (recenter 0)
+ t)
(goto-char (- (point-max) 1))
(while (point-invisible-p)
- (backward-char)))
- (recenter 0))
+ (backward-char))
+ (recenter 0)
+ nil))
(defun notmuch-show-find-next-message ()
"Returns the position of the next message in the buffer.
@@ -445,14 +464,9 @@ there are no more unread messages past the current point."
(notmuch-show-next-message)))
(defun notmuch-show-next-open-message ()
- "Advance to the next message which is not hidden.
-
-If read messages are currently hidden, advance to the next unread
-message. Otherwise, advance to the next message."
- (if (or (memq 'notmuch-show-body-read buffer-invisibility-spec)
- (assq 'notmuch-show-body-read buffer-invisibility-spec))
- (notmuch-show-next-unread-message)
- (notmuch-show-next-message)))
+ "Advance to the next open message (that is, body is not invisible)."
+ (while (and (notmuch-show-next-message)
+ (not (notmuch-show-message-open-p)))))
(defun notmuch-show-previous-message ()
"Backup to the beginning of the previous message in the buffer.
@@ -486,13 +500,13 @@ it."
(point))))
(defun notmuch-show-mark-read-then-next-open-message ()
- "Remove unread tag from current message, then advance to next unread message."
+ "Remove unread tag from this message, then advance to next open message."
(interactive)
(notmuch-show-remove-tag "unread")
(notmuch-show-next-open-message))
(defun notmuch-show-rewind ()
- "Do reverse scrolling compared to `notmuch-show-advance-marking-read-and-archiving'
+ "Backup through the thread, (reverse scrolling compared to \\[notmuch-show-advance-marking-read-and-archiving]).
Specifically, if the beginning of the previous email is fewer
than `window-height' lines from the current point, move to it
@@ -514,7 +528,7 @@ any effects from previous calls to
(notmuch-show-previous-message))))
(defun notmuch-show-advance-marking-read-and-archiving ()
- "Advance through buffer, marking read and archiving.
+ "Advance through thread, marking read and archiving.
This command is intended to be one of the simplest ways to
process a thread of email. It does the following:
@@ -552,7 +566,7 @@ which this thread was originally shown."
(goto-char (button-start (previous-button (point)))))
(defun notmuch-toggle-invisible-action (cite-button)
- (let ((invis-spec (button-get button 'invisibility-spec)))
+ (let ((invis-spec (button-get cite-button 'invisibility-spec)))
(if (invisible-p invis-spec)
(remove-from-invisibility-spec invis-spec)
(add-to-invisibility-spec invis-spec)
@@ -560,14 +574,19 @@ which this thread was originally shown."
(force-window-update)
(redisplay t))
-(define-button-type 'notmuch-button-invisibility-toggle-type 'action 'notmuch-toggle-invisible-action 'follow-link t)
+(define-button-type 'notmuch-button-invisibility-toggle-type
+ 'action 'notmuch-toggle-invisible-action
+ 'follow-link t
+ 'face "default")
(define-button-type 'notmuch-button-citation-toggle-type 'help-echo "mouse-1, RET: Show citation"
:supertype 'notmuch-button-invisibility-toggle-type)
(define-button-type 'notmuch-button-signature-toggle-type 'help-echo "mouse-1, RET: Show signature"
:supertype 'notmuch-button-invisibility-toggle-type)
(define-button-type 'notmuch-button-headers-toggle-type 'help-echo "mouse-1, RET: Show headers"
:supertype 'notmuch-button-invisibility-toggle-type)
-(define-button-type 'notmuch-button-body-toggle-type 'help-echo "mouse-1, RET: Show message"
+(define-button-type 'notmuch-button-body-toggle-type
+ 'help-echo "mouse-1, RET: Show message"
+ 'face 'notmuch-message-summary-face
:supertype 'notmuch-button-invisibility-toggle-type)
(defun notmuch-show-markup-citations-region (beg end depth)
@@ -665,7 +684,20 @@ which this thread was originally shown."
(notmuch-show-markup-part
beg end depth mime-message))))))
-(defun notmuch-show-markup-body (depth btn)
+(defun notmuch-show-markup-body (depth match btn)
+ "Markup a message body, (indenting, buttonizing citations,
+etc.), and conditionally hiding the body itself if the message
+has been read and does not match the current search.
+
+DEPTH specifies the depth at which this message appears in the
+tree of the current thread, (the top-level messages have depth 0
+and each reply increases depth by 1). MATCH indicates whether
+this message is regarded as matching the current search. BTN is
+the button which is used to toggle the visibility of this
+message.
+
+When this function is called, point must be within the message, but
+before the delimiter marking the beginning of the body."
(re-search-forward notmuch-show-body-begin-regexp)
(forward-line)
(let ((beg (point-marker)))
@@ -676,86 +708,95 @@ which this thread was originally shown."
(overlay-put (make-overlay beg end)
'invisible invis-spec)
(button-put btn 'invisibility-spec invis-spec)
- (if (not (notmuch-show-message-unread-p))
+ (if (not (or (notmuch-show-message-unread-p) match))
(add-to-invisibility-spec invis-spec)))
(set-marker beg nil)
(set-marker end nil)
)))
+
(defun notmuch-fontify-headers ()
- (progn
- (if (looking-at "[Tt]o:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-to))
+ (while (looking-at "[[:space:]]")
+ (forward-char))
+ (if (looking-at "[Tt]o:")
+ (progn
+ (overlay-put (make-overlay (point) (re-search-forward ":"))
+ 'face 'message-header-name)
+ (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+ 'face 'message-header-to))
(if (looking-at "[B]?[Cc][Cc]:")
(progn
(overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-cc))
- (if (looking-at "[Ss]ubject:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-subject))
- (if (looking-at "[Ff]rom:")
- (progn
- (overlay-put (make-overlay (point) (re-search-forward ":"))
- 'face 'message-header-name)
- (overlay-put (make-overlay (point) (re-search-forward ".*$"))
- 'face 'message-header-other))))))))
-
-(defun notmuch-show-markup-header (depth)
+ 'face 'message-header-name)
+ (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+ 'face 'message-header-cc))
+ (if (looking-at "[Ss]ubject:")
+ (progn
+ (overlay-put (make-overlay (point) (re-search-forward ":"))
+ 'face 'message-header-name)
+ (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+ 'face 'message-header-subject))
+ (if (looking-at "[Ff]rom:")
+ (progn
+ (overlay-put (make-overlay (point) (re-search-forward ":"))
+ 'face 'message-header-name)
+ (overlay-put (make-overlay (point) (re-search-forward ".*$"))
+ 'face 'message-header-other)))))))
+
+(defun notmuch-show-markup-header (message-begin depth)
+ "Buttonize and decorate faces in a message header.
+
+MESSAGE-BEGIN is the position of the absolute first character in
+the message (including all delimiters that will end up being
+invisible etc.). This is to allow a button to reliably extend to
+the beginning of the message even if point is positioned at an
+invisible character (such as the beginning of the buffer).
+
+DEPTH specifies the depth at which this message appears in the
+tree of the current thread, (the top-level messages have depth 0
+and each reply increases depth by 1)."
(re-search-forward notmuch-show-header-begin-regexp)
(forward-line)
(let ((beg (point-marker))
+ (summary-end (copy-marker (line-beginning-position 2)))
+ (subject-end (copy-marker (line-end-position 2)))
+ (invis-spec (make-symbol "notmuch-show-header"))
(btn nil))
- (end-of-line)
- ; Inverse video for subject
- (overlay-put (make-overlay beg (point)) 'face '(:inverse-video t))
- (setq btn (make-button beg (point) :type 'notmuch-button-body-toggle-type))
- (forward-line 1)
- (end-of-line)
- (let ((beg-hidden (point-marker)))
- (re-search-forward notmuch-show-header-end-regexp)
- (beginning-of-line)
- (let ((end (point-marker)))
- (goto-char beg)
- (forward-line)
- (while (looking-at "[A-Za-z][-A-Za-z0-9]*:")
- (beginning-of-line)
- (notmuch-fontify-headers)
- (forward-line)
- )
- (indent-rigidly beg end depth)
- (let ((invis-spec (make-symbol "notmuch-show-header")))
- (add-to-invisibility-spec (cons invis-spec t))
- (overlay-put (make-overlay beg-hidden end)
- 'invisible invis-spec)
- (goto-char beg)
- (forward-line)
- (make-button (line-beginning-position) (line-end-position)
- 'invisibility-spec (cons invis-spec t)
- :type 'notmuch-button-headers-toggle-type))
- (goto-char end)
- (insert "\n")
- (set-marker beg nil)
- (set-marker beg-hidden nil)
- (set-marker end nil)
- ))
- btn))
+ (re-search-forward notmuch-show-header-end-regexp)
+ (beginning-of-line)
+ (let ((end (point-marker)))
+ (indent-rigidly beg end depth)
+ (goto-char beg)
+ (setq btn (make-button message-begin summary-end :type 'notmuch-button-body-toggle-type))
+ (forward-line)
+ (add-to-invisibility-spec invis-spec)
+ (overlay-put (make-overlay subject-end end)
+ 'invisible invis-spec)
+ (make-button (line-beginning-position) subject-end
+ 'invisibility-spec invis-spec
+ :type 'notmuch-button-headers-toggle-type)
+ (while (looking-at "[[:space:]]*[A-Za-z][-A-Za-z0-9]*:")
+ (beginning-of-line)
+ (notmuch-fontify-headers)
+ (forward-line)
+ )
+ (goto-char end)
+ (insert "\n")
+ (set-marker beg nil)
+ (set-marker summary-end nil)
+ (set-marker subject-end nil)
+ (set-marker end nil)
+ )
+ btn))
(defun notmuch-show-markup-message ()
(if (re-search-forward notmuch-show-message-begin-regexp nil t)
- (progn
- (re-search-forward notmuch-show-depth-regexp)
+ (let ((message-begin (match-beginning 0)))
+ (re-search-forward notmuch-show-depth-match-regexp)
(let ((depth (string-to-number (buffer-substring (match-beginning 1) (match-end 1))))
+ (match (string= "1" (buffer-substring (match-beginning 2) (match-end 2))))
(btn nil))
- (setq btn (notmuch-show-markup-header depth))
- (notmuch-show-markup-body depth btn)))
+ (setq btn (notmuch-show-markup-header message-begin depth))
+ (notmuch-show-markup-body depth match btn)))
(goto-char (point-max))))
(defun notmuch-show-hide-markers ()
@@ -775,6 +816,72 @@ which this thread was originally shown."
(notmuch-show-markup-message)))
(notmuch-show-hide-markers))
+(defun notmuch-documentation-first-line (symbol)
+ "Return the first line of the documentation string for SYMBOL."
+ (let ((doc (documentation symbol)))
+ (if doc
+ (with-temp-buffer
+ (insert (documentation symbol t))
+ (goto-char (point-min))
+ (let ((beg (point)))
+ (end-of-line)
+ (buffer-substring beg (point))))
+ "")))
+
+(defun notmuch-prefix-key-description (key)
+ "Given a prefix key code, return a human-readable string representation.
+
+This is basically just `format-kbd-macro' but we also convert ESC to M-."
+ (let ((desc (format-kbd-macro (vector key))))
+ (if (string= desc "ESC")
+ "M-"
+ (concat desc " "))))
+
+; I would think that emacs would have code handy for walking a keymap
+; and generating strings for each key, and I would prefer to just call
+; that. But I couldn't find any (could be all implemented in C I
+; suppose), so I wrote my own here.
+(defun notmuch-substitute-one-command-key-with-prefix (prefix binding)
+ "For a key binding, return a string showing a human-readable
+representation of the prefixed key as well as the first line of
+documentation from the bound function.
+
+For a mouse binding, return nil."
+ (let ((key (car binding))
+ (action (cdr binding)))
+ (if (mouse-event-p key)
+ nil
+ (if (keymapp action)
+ (let ((substitute (apply-partially 'notmuch-substitute-one-command-key-with-prefix (notmuch-prefix-key-description key))))
+ (mapconcat substitute (cdr action) "\n"))
+ (concat prefix (format-kbd-macro (vector key))
+ "\t"
+ (notmuch-documentation-first-line action))))))
+
+(defalias 'notmuch-substitute-one-command-key
+ (apply-partially 'notmuch-substitute-one-command-key-with-prefix nil))
+
+(defun notmuch-substitute-command-keys (doc)
+ "Like `substitute-command-keys' but with documentation, not function names."
+ (let ((beg 0))
+ (while (string-match "\\\\{\\([^}[:space:]]*\\)}" doc beg)
+ (let ((map (substring doc (match-beginning 1) (match-end 1))))
+ (setq doc (replace-match (mapconcat 'notmuch-substitute-one-command-key
+ (cdr (symbol-value (intern map))) "\n") 1 1 doc)))
+ (setq beg (match-end 0)))
+ doc))
+
+(defun notmuch-help ()
+ "Display help for the current notmuch mode."
+ (interactive)
+ (let* ((mode major-mode)
+ (doc (substitute-command-keys (notmuch-substitute-command-keys (documentation mode t)))))
+ (with-current-buffer (generate-new-buffer "*notmuch-help*")
+ (insert doc)
+ (goto-char (point-min))
+ (set-buffer-modified-p nil)
+ (view-buffer (current-buffer) 'kill-buffer-if-not-modified))))
+
;;;###autoload
(defun notmuch-show-mode ()
"Major mode for viewing a thread with notmuch.
@@ -783,22 +890,28 @@ This buffer contains the results of the \"notmuch show\" command
for displaying a single thread of email from your email archives.
By default, various components of email messages, (citations,
-signatures, already-read messages), are invisible to help you
-focus on the most important things, (new text from unread
-messages). See the various commands below for toggling the
-visibility of hidden components.
-
-The `notmuch-show-next-message' and
-`notmuch-show-previous-message' commands, (bound to 'n' and 'p by
-default), allow you to navigate to the next and previous
-messages. Each time you navigate away from a message with
-`notmuch-show-next-message' the current message will have its
-\"unread\" tag removed.
-
-You can add or remove tags from the current message with '+' and
-'-'. You can also archive all messages in the current
-view, (remove the \"inbox\" tag from each), with
-`notmuch-show-archive-thread' (bound to 'a' by default).
+signatures, already-read messages), are hidden. You can make
+these parts visible by clicking with the mouse button or by
+pressing RET after positioning the cursor on a hidden part, (for
+which \\[notmuch-show-next-button] and \\[notmuch-show-previous-button] are helpful).
+
+Reading the thread sequentially is well-supported by pressing
+\\[notmuch-show-advance-marking-read-and-archiving]. This will scroll the current message (if necessary),
+advance to the next message, or advance to the next thread (if
+already on the last message of a thread). As each message is
+scrolled away its \"unread\" tag will be removed, and as each
+thread is scrolled away the \"inbox\" tag will be removed from
+each message in the thread.
+
+Other commands are available to read or manipulate the thread more
+selectively, (such as '\\[notmuch-show-next-message]' and '\\[notmuch-show-previous-message]' to advance to messages without
+removing any tags, and '\\[notmuch-show-archive-thread]' to archive an entire thread without
+scrolling through with \\[notmuch-show-advance-marking-read-and-archiving]).
+
+You can add or remove arbitary tags from the current message with
+'\\[notmuch-show-add-tag]' or '\\[notmuch-show-remove-tag]'.
+
+All currently available key bindings:
\\{notmuch-show-mode-map}"
(interactive)
@@ -843,7 +956,8 @@ The optional PARENT-BUFFER is the notmuch-search buffer from
which this notmuch-show command was executed, (so that the next
thread from that buffer can be show when done with this one)."
(interactive "sNotmuch show: ")
- (let ((buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*"))))
+ (let ((query notmuch-search-query-string)
+ (buffer (get-buffer-create (concat "*notmuch-show-" thread-id "*"))))
(switch-to-buffer buffer)
(notmuch-show-mode)
(set (make-local-variable 'notmuch-show-parent-buffer) parent-buffer)
@@ -855,30 +969,13 @@ thread from that buffer can be show when done with this one)."
(erase-buffer)
(goto-char (point-min))
(save-excursion
- (call-process notmuch-command nil t nil "show" thread-id)
+ (call-process notmuch-command nil t nil "show" "--entire-thread" thread-id "and (" query ")")
(notmuch-show-markup-messages)
)
(run-hooks 'notmuch-show-hook)
- ; Move straight to the first unread message
- (if (not (notmuch-show-message-unread-p))
- (progn
- (notmuch-show-next-unread-message)
- ; But if there are no unread messages, go back to the
- ; beginning of the buffer, and open up the bodies of all
- ; read message.
- (if (not (notmuch-show-message-unread-p))
- (progn
- (goto-char (point-min))
- (let ((btn (forward-button 1)))
- (while btn
- (if (button-has-type-p btn 'notmuch-button-body-toggle-type)
- (push-button))
- (condition-case err
- (setq btn (forward-button 1))
- (error (setq btn nil)))
- ))
- (beginning-of-buffer)
- ))))
+ ; Move straight to the first open message
+ (if (not (notmuch-show-message-open-p))
+ (notmuch-show-next-open-message))
)))
(defvar notmuch-search-authors-width 40
@@ -886,30 +983,29 @@ thread from that buffer can be show when done with this one)."
(defvar notmuch-search-mode-map
(let ((map (make-sparse-keymap)))
- (define-key map "a" 'notmuch-search-archive-thread)
- (define-key map "b" 'notmuch-search-scroll-down)
- (define-key map "f" 'notmuch-search-filter)
- (define-key map "m" 'message-mail)
- (define-key map "n" 'next-line)
- (define-key map "o" 'notmuch-search-toggle-order)
- (define-key map "p" 'previous-line)
+ (define-key map "?" 'notmuch-help)
(define-key map "q" 'kill-this-buffer)
+ (define-key map "x" 'kill-this-buffer)
+ (define-key map (kbd "<DEL>") 'notmuch-search-scroll-down)
+ (define-key map "b" 'notmuch-search-scroll-down)
+ (define-key map " " 'notmuch-search-scroll-up)
+ (define-key map "<" 'notmuch-search-first-thread)
+ (define-key map ">" 'notmuch-search-last-thread)
+ (define-key map "p" 'notmuch-search-previous-thread)
+ (define-key map "n" 'notmuch-search-next-thread)
(define-key map "r" 'notmuch-search-reply-to-thread)
+ (define-key map "m" 'message-mail)
(define-key map "s" 'notmuch-search)
+ (define-key map "o" 'notmuch-search-toggle-order)
+ (define-key map "=" 'notmuch-search-refresh-view)
(define-key map "t" 'notmuch-search-filter-by-tag)
- (define-key map "x" 'kill-this-buffer)
- (define-key map (kbd "RET") 'notmuch-search-show-thread)
+ (define-key map "f" 'notmuch-search-filter)
(define-key map [mouse-1] 'notmuch-search-show-thread)
- (define-key map "+" 'notmuch-search-add-tag)
- (define-key map "-" 'notmuch-search-remove-tag)
(define-key map "*" 'notmuch-search-operate-all)
- (define-key map "<" 'beginning-of-buffer)
- (define-key map ">" 'notmuch-search-goto-last-thread)
- (define-key map "=" 'notmuch-search-refresh-view)
- (define-key map "\M->" 'notmuch-search-goto-last-thread)
- (define-key map " " 'notmuch-search-scroll-up)
- (define-key map (kbd "<DEL>") 'notmuch-search-scroll-down)
- (define-key map "?" 'describe-mode)
+ (define-key map "a" 'notmuch-search-archive-thread)
+ (define-key map "-" 'notmuch-search-remove-tag)
+ (define-key map "+" 'notmuch-search-add-tag)
+ (define-key map (kbd "RET") 'notmuch-search-show-thread)
map)
"Keymap for \"notmuch search\" buffers.")
(fset 'notmuch-search-mode-map notmuch-search-mode-map)
@@ -918,16 +1014,17 @@ thread from that buffer can be show when done with this one)."
(defvar notmuch-search-oldest-first t
"Show the oldest mail first in the search-mode")
+(defvar notmuch-search-disjunctive-regexp "\\<[oO][rR]\\>")
(defun notmuch-search-scroll-up ()
- "Scroll up, moving point to last message in thread if at end."
+ "Move forward through search results by one window's worth."
(interactive)
(condition-case nil
(scroll-up nil)
- ((end-of-buffer) (notmuch-search-goto-last-thread))))
+ ((end-of-buffer) (notmuch-search-last-thread))))
(defun notmuch-search-scroll-down ()
- "Scroll down, moving point to first message in thread if at beginning."
+ "Move backward through the search results by one window's worth."
(interactive)
; I don't know why scroll-down doesn't signal beginning-of-buffer
; the way that scroll-up signals end-of-buffer, but c'est la vie.
@@ -937,16 +1034,37 @@ thread from that buffer can be show when done with this one)."
; directly to that position. (We have to count lines since the
; window-start position is not the same as point-min due to the
; invisible thread-ID characters on the first line.
- (if (equal (count-lines (point-min) (window-start)) 1)
- (goto-char (window-start))
+ (if (equal (count-lines (point-min) (window-start)) 0)
+ (goto-char (point-min))
(scroll-down nil)))
-(defun notmuch-search-goto-last-thread ()
- "Move point to the last thread in the buffer."
+(defun notmuch-search-next-thread ()
+ "Select the next thread in the search results."
+ (interactive)
+ (forward-line 1))
+
+(defun notmuch-search-previous-thread ()
+ "Select the previous thread in the search results."
(interactive)
- (goto-char (point-max))
(forward-line -1))
+(defun notmuch-search-last-thread ()
+ "Select the last thread in the search results."
+ (interactive)
+ (goto-char (point-max))
+ (forward-line -2))
+
+(defun notmuch-search-first-thread ()
+ "Select the first thread in the search results."
+ (interactive)
+ (goto-char (point-min)))
+
+(defface notmuch-message-summary-face
+ '((((class color) (background light)) (:background "#f0f0f0"))
+ (((class color) (background dark)) (:background "#303030")))
+ "Face for the single-line message summary in notmuch-show-mode."
+ :group 'notmuch)
+
(defface notmuch-tag-face
'((((class color)
(background dark))
@@ -966,22 +1084,27 @@ thread from that buffer can be show when done with this one)."
;;;###autoload
(defun notmuch-search-mode ()
- "Major mode for searching mail with notmuch.
+ "Major mode displaying results of a notmuch search.
This buffer contains the results of a \"notmuch search\" of your
email archives. Each line in the buffer represents a single
-thread giving a relative date for the thread and a subject.
+thread giving a summary of the thread (a relative date, the
+number of matched messages and total messages in the thread,
+participants in the thread, a representative subject line, and
+any tags).
-Pressing RET on any line displays that thread. The '+' and '-'
-keys can be used to add or remove tags from a thread. The 'a' key
-is a convenience key for archiving a thread (removing the
-\"inbox\" tag).
+Pressing \\[notmuch-search-show-thread] on any line displays that thread. The '\\[notmuch-search-add-tag]' and '\\[notmuch-search-remove-tag]'
+keys can be used to add or remove tags from a thread. The '\\[notmuch-search-archive-thread]' key
+is a convenience for archiving a thread (removing the \"inbox\"
+tag). The '\\[notmuch-search-operate-all]' key can be used to add or remove a tag from all
+threads in the current buffer.
-Other useful commands are `notmuch-search-filter' for filtering
-the current search based on an additional query string,
-`notmuch-search-filter-by-tag' for filtering to include only
-messages with a given tag, and `notmuch-search' to execute a new,
-global search.
+Other useful commands are '\\[notmuch-search-filter]' for filtering the current search
+based on an additional query string, '\\[notmuch-search-filter-by-tag]' for filtering to include
+only messages with a given tag, and '\\[notmuch-search]' to execute a new, global
+search.
+
+Complete list of currently available key bindings:
\\{notmuch-search-mode-map}"
(interactive)
@@ -998,12 +1121,11 @@ global search.
(if (not notmuch-tag-face-alist)
(add-to-list 'notmuch-search-font-lock-keywords (list
"(\\([^)]*\\))$" '(1 'notmuch-tag-face)))
- (progn
- (setq notmuch-search-tags (mapcar 'car notmuch-tag-face-alist))
- (loop for notmuch-search-tag in notmuch-search-tags
- do (add-to-list 'notmuch-search-font-lock-keywords (list
- (concat "([^)]*\\(" notmuch-search-tag "\\)[^)]*)$")
- `(1 ,(cdr (assoc notmuch-search-tag notmuch-tag-face-alist))))))))
+ (let ((notmuch-search-tags (mapcar 'car notmuch-tag-face-alist)))
+ (loop for notmuch-search-tag in notmuch-search-tags
+ do (add-to-list 'notmuch-search-font-lock-keywords (list
+ (concat "([^)]*\\(" notmuch-search-tag "\\)[^)]*)$")
+ `(1 ,(cdr (assoc notmuch-search-tag notmuch-tag-face-alist))))))))
(set (make-local-variable 'font-lock-defaults)
'(notmuch-search-font-lock-keywords t)))
@@ -1012,6 +1134,7 @@ global search.
(get-text-property (point) 'notmuch-search-thread-id))
(defun notmuch-search-show-thread ()
+ "Display the currently selected thread."
(interactive)
(let ((thread-id (notmuch-search-find-thread-id)))
(if (> (length thread-id) 0)
@@ -1064,25 +1187,29 @@ and will also appear in a buffer named \"*Notmuch errors*\"."
(split-string (buffer-substring beg end))))))
(defun notmuch-search-add-tag (tag)
- "Add a tag to messages in the current thread matching the
-active query."
+ "Add a tag to the currently selected thread.
+
+The tag is added to messages in the currently selected thread
+which match the current search terms."
(interactive
(list (notmuch-select-tag-with-completion "Tag to add: ")))
(notmuch-call-notmuch-process "tag" (concat "+" tag) (notmuch-search-find-thread-id) " and " notmuch-search-query-string)
(notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<))))
(defun notmuch-search-remove-tag (tag)
- "Remove a tag from messages in the current thread matching the
-active query."
+ "Remove a tag from the currently selected thread.
+
+The tag is removed from messages in the currently selected thread
+which match the current search terms."
(interactive
(list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-search-find-thread-id))))
(notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id) " and " notmuch-search-query-string)
(notmuch-search-set-tags (delete tag (notmuch-search-get-tags))))
(defun notmuch-search-archive-thread ()
- "Archive the current thread (remove its \"inbox\" tag).
+ "Archive the currently selected thread (remove its \"inbox\" tag).
-This function advances point to the next line when finished."
+This function advances the next thread when finished."
(interactive)
(notmuch-search-remove-tag "inbox")
(forward-line))
@@ -1136,12 +1263,12 @@ This function advances point to the next line when finished."
(delete-process proc))))
(defun notmuch-search-operate-all (action)
- "Operate on all messages matching the current query. Any
-number of whitespace separated actions can be given. Each action
-must have one of the two forms
+ "Add/remove tags from all matching messages.
- +tagname Add the tag `tagname'
- -tagname Remove the tag `tagname'
+Tis command adds or removes tags from all messages matching the
+current search terms. When called interactively, this command
+will prompt for tags to be added or removed. Tags prefixed with
+'+' will be added and tags prefixed with '-' will be removed.
Each character of the tag name may consist of alphanumeric
characters as well as `_.+-'.
@@ -1227,7 +1354,8 @@ search."
Runs a new search matching only messages that match both the
current search results AND the additional query string provided."
(interactive "sFilter search: ")
- (notmuch-search (concat notmuch-search-query-string " and " query) notmuch-search-oldest-first))
+ (let ((grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query) (concat "( " query " )") query)))
+ (notmuch-search (concat notmuch-search-query-string " and " grouped-query) notmuch-search-oldest-first)))
(defun notmuch-search-filter-by-tag (tag)
"Filter the current search results based on a single tag.
@@ -1249,16 +1377,17 @@ current search results AND that are tagged with the given tag."
(defvar notmuch-folder-mode-map
(let ((map (make-sparse-keymap)))
- (define-key map "n" 'next-line)
- (define-key map "p" 'previous-line)
+ (define-key map "?" 'notmuch-help)
(define-key map "x" 'kill-this-buffer)
(define-key map "q" 'kill-this-buffer)
- (define-key map "s" 'notmuch-search)
- (define-key map (kbd "RET") 'notmuch-folder-show-search)
- (define-key map "<" 'beginning-of-buffer)
+ (define-key map ">" 'notmuch-folder-last)
+ (define-key map "<" 'notmuch-folder-first)
(define-key map "=" 'notmuch-folder)
- (define-key map "?" 'describe-mode)
+ (define-key map "s" 'notmuch-search)
(define-key map [mouse-1] 'notmuch-folder-show-search)
+ (define-key map (kbd "RET") 'notmuch-folder-show-search)
+ (define-key map "p" 'notmuch-folder-previous)
+ (define-key map "n" 'notmuch-folder-next)
map)
"Keymap for \"notmuch folder\" buffers.")
@@ -1272,12 +1401,26 @@ current search results AND that are tagged with the given tag."
(defun notmuch-folder-mode ()
"Major mode for showing notmuch 'folders'.
-This buffer contains a list of messages counts returned by a
-customizable set of searches of your email archives. Each line
-in the buffer shows the search terms and the resulting message count.
+This buffer contains a list of message counts returned by a
+customizable set of searches of your email archives. Each line in
+the buffer shows the name of a saved search and the resulting
+message count.
Pressing RET on any line opens a search window containing the
-results for the search terms in that line.
+results for the saved search on that line.
+
+Here is an example of how the search list could be
+customized, (the following text would be placed in your ~/.emacs
+file):
+
+(setq notmuch-folders '((\"inbox\" . \"tag:inbox\")
+ (\"unread\" . \"tag:inbox AND tag:unread\")
+ (\"notmuch\" . \"tag:inbox AND to:notmuchmail.org\")))
+
+Of course, you can have any number of folders, each configured
+with any supported search terms (see \"notmuch help search-terms\").
+
+Currently available key bindings:
\\{notmuch-folder-mode-map}"
(interactive)
@@ -1289,6 +1432,29 @@ results for the search terms in that line.
mode-name "notmuch-folder")
(setq buffer-read-only t))
+(defun notmuch-folder-next ()
+ "Select the next folder in the list."
+ (interactive)
+ (forward-line 1)
+ (if (eobp)
+ (forward-line -1)))
+
+(defun notmuch-folder-previous ()
+ "Select the previous folder in the list."
+ (interactive)
+ (forward-line -1))
+
+(defun notmuch-folder-first ()
+ "Select the first folder in the list."
+ (interactive)
+ (goto-char (point-min)))
+
+(defun notmuch-folder-last ()
+ "Select the last folder in the list."
+ (interactive)
+ (goto-char (point-max))
+ (forward-line -1))
+
(defun notmuch-folder-add (folders)
(if folders
(let ((name (car (car folders)))
diff --git a/vim/plugin/notmuch.vim b/vim/plugin/notmuch.vim
index b415f50..a226f20 100644
--- a/vim/plugin/notmuch.vim
+++ b/vim/plugin/notmuch.vim
@@ -275,6 +275,7 @@ function! s:NM_search_show_thread(everything)
call add(words, ')')
endif
call <SID>NM_cmd_show(words)
+ let b:nm_show_everything = a:everything
endfunction
function! s:NM_search_prompt()
@@ -408,7 +409,7 @@ endfunction
function! s:NM_cmd_show(words)
let prev_bufnr = bufnr('%')
- let data = s:NM_run(['show'] + a:words)
+ let data = s:NM_run(['show', '--entire-thread'] + a:words)
let lines = split(data, "\n")
let info = s:NM_cmd_show_parse(lines)
@@ -430,6 +431,7 @@ function! s:NM_cmd_show(words)
endfunction
function! s:NM_show_previous(can_change_thread, find_matching)
+ let everything = exists('b:nm_show_everything') ? b:nm_show_everything : 0
let info = b:nm_raw_info
let lnum = line('.')
for msg in reverse(copy(info['msgs']))
@@ -450,7 +452,7 @@ function! s:NM_show_previous(can_change_thread, find_matching)
call <SID>NM_kill_this_buffer()
if line('.') > 1
norm k
- call <SID>NM_search_show_thread()
+ call <SID>NM_search_show_thread(everything)
norm G
call <SID>NM_show_previous(0, a:find_matching)
else
@@ -479,10 +481,11 @@ function! s:NM_show_next(can_change_thread, find_matching)
endfunction
function! s:NM_show_next_thread()
+ let everything = exists('b:nm_show_everything') ? b:nm_show_everything : 0
call <SID>NM_kill_this_buffer()
if line('.') != line('$')
norm j
- call <SID>NM_search_show_thread()
+ call <SID>NM_search_show_thread(everything)
else
echo 'No more messages.'
endif