merge changes from upstream

This commit is contained in:
Jameson Graef Rollins 2009-12-05 01:19:53 -05:00
commit e72a6176e3
26 changed files with 958 additions and 357 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
Makefile.config
TAGS
tags
*cscope*

View file

@ -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)
override LDFLAGS += \
$(shell pkg-config --libs glib-2.0 gmime-2.4 talloc) \
$(shell xapian-config --libs)
all: notmuch notmuch.1.gz
# Include our local Makefile.local first so that its first target is default
include Makefile.local
include lib/Makefile.local
# 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
# And get user settings from the output of configure
include Makefile.config
include lib/Makefile.local
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 $@.$$$$

View file

@ -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

31
TODO
View file

@ -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).

5
compat/Makefile Normal file
View file

@ -0,0 +1,5 @@
all:
$(MAKE) -C .. all
clean:
$(MAKE) -C .. clean

8
compat/Makefile.local Normal file
View file

@ -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

41
compat/compat.h Normal file
View file

@ -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 */

133
compat/getdelim.c Normal file
View file

@ -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;
}

29
compat/getline.c Normal file
View file

@ -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);
}

5
config/README Normal file
View file

@ -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.).

13
config/have_getline.c Normal file
View file

@ -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);
}

233
configure vendored
View file

@ -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
else
echo "Checking for zlib development files... No."
have_zlib=0
errors=$((errors + 1))
fi
printf "Checking for valgrind development files... "
if pkg-config --modversion valgrind > /dev/null 2>&1; then
echo "Checking for valgrind development files... Yes."
have_valgrind=-DHAVE_VALGRIND
printf "Yes.\n"
have_valgrind=1
valgrind_cflags=$(pkg-config --cflags valgrind)
else
echo "Checking for valgrind development files... No."
have_valgrind=
printf "No (but that's fine).\n"
have_valgrind=0
fi
if pkg-config --modversion emacs > /dev/null 2>&1; then
emacs_lispdir=$(pkg-config emacs --variable sitepkglispdir)
else
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

View file

@ -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

View file

@ -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 *

View file

@ -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;
}

View file

@ -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"

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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));

View file

@ -104,7 +104,10 @@ notmuch_setup_command (unused (void *ctx),
do { \
printf (format, ##__VA_ARGS__); \
fflush (stdout); \
getline (&response, &response_size, stdin); \
if (getline (&response, &response_size, stdin) < 0) { \
printf ("Exiting.\n"); \
exit (1); \
} \
chomp_newline (response); \
} while (0)

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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"

View file

@ -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)
(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,13 +708,15 @@ 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
(while (looking-at "[[:space:]]")
(forward-char))
(if (looking-at "[Tt]o:")
(progn
(overlay-put (make-overlay (point) (re-search-forward ":"))
@ -706,56 +740,63 @@ which this thread was originally shown."
(overlay-put (make-overlay (point) (re-search-forward ":"))
'face 'message-header-name)
(overlay-put (make-overlay (point) (re-search-forward ".*$"))
'face 'message-header-other))))))))
'face 'message-header-other)))))))
(defun notmuch-show-markup-header (depth)
(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)))
(indent-rigidly beg end depth)
(goto-char beg)
(setq btn (make-button message-begin summary-end :type 'notmuch-button-body-toggle-type))
(forward-line)
(while (looking-at "[A-Za-z][-A-Za-z0-9]*:")
(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)
)
(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 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.
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).
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.
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.
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).
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 "r" 'notmuch-search-reply-to-thread)
(define-key map "s" 'notmuch-search)
(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 [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 "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 "f" 'notmuch-search-filter)
(define-key map [mouse-1] 'notmuch-search-show-thread)
(define-key map "*" 'notmuch-search-operate-all)
(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,15 +1034,36 @@ 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)
(forward-line -1))
(defun notmuch-search-last-thread ()
"Select the last thread in the search results."
(interactive)
(goto-char (point-max))
(forward-line -1))
(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)
@ -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,8 +1121,7 @@ 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))
(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 "\\)[^)]*)$")
@ -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)))

View file

@ -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