Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 7DBA8431FAF for ; Thu, 29 Mar 2012 06:36:27 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none] autolearn=disabled Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id YYnt+7Jfxo0f for ; Thu, 29 Mar 2012 06:36:25 -0700 (PDT) Received: from upsilon.hackadomia.org (upsilon.hackadomia.org [91.121.245.170]) by olra.theworths.org (Postfix) with ESMTP id 12D9A431FAE for ; Thu, 29 Mar 2012 06:36:25 -0700 (PDT) Received: from usha.takhisis.invalid (eduroam-0-228.enst.fr [137.194.56.228]) by upsilon.hackadomia.org (Postfix) with ESMTPSA id 8E87D1015C; Thu, 29 Mar 2012 15:36:22 +0200 (CEST) Received: by usha.takhisis.invalid (Postfix, from userid 1000) id 513FD6808C2; Thu, 29 Mar 2012 15:35:38 +0200 (CEST) Date: Thu, 29 Mar 2012 15:35:38 +0200 From: Stefano Zacchiroli To: notmuch@notmuchmail.org Subject: [PATCH v2]: contrib/notmuch-mutt Message-ID: <20120329133538.GA12117@upsilon.cc> References: <87iphszuv1.fsf@zancas.localnet> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="gj572EiMnwbLXET9" Content-Disposition: inline In-Reply-To: <87iphszuv1.fsf@zancas.localnet> User-Agent: Mutt/1.5.21 (2010-09-15) Cc: Ben Boeckel , 628018-quiet@bugs.debian.org X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 29 Mar 2012 13:36:27 -0000 --gj572EiMnwbLXET9 Content-Type: multipart/mixed; boundary="qDbXVdCdHGoSgWSk" Content-Disposition: inline --qDbXVdCdHGoSgWSk Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Sun, Mar 25, 2012 at 10:13:38AM -0300, David Bremner wrote: > In the mean time, we at least have a contrib directory, and I think > mutt-notmuch would be welcome there. Could you (or somebody) make a > reasonable size patch series that adds it to contrib/mutt-notmuch > against current master. The patch series should be sent to the > upstream mailing list notmuch@notmuchmail.org for review. >=20 > On the debian side your patch series could also include the necessary > changes to make a new binary package. >=20 > Notmuch itself is already GPL3+ so no hassles there (for once). Here is an updated set of patches, which I consider final for inclusion of notmuch-mutt as part of notmuch contrib/. AFAICT I've implemented all changes that have been requested either on this list or in private mail to me: renaming, Debian packaging, XDG userdir support, shell quoting in all notmuch calls. TTBOMK, no further changes have been requested. Can some kind soul "git am" the attached patches to notmuch Git repo? If OTOH you have further request for changes, please let me know. I'll be happy to act on them. TIA, Cheers. --=20 Stefano Zacchiroli zack@{upsilon.cc,pps.jussieu.fr,debian.org} . o . Ma=EEtre de conf=E9rences ...... http://upsilon.cc/zack ...... . . o Debian Project Leader ....... @zack on identi.ca ....... o o o =AB the first rule of tautology club is the first rule of tautology club =BB --qDbXVdCdHGoSgWSk Content-Type: text/x-diff; charset=iso-8859-1 Content-Disposition: attachment; filename="0001-contrib-new-mutt-notmuch-utility-for-Mutt-integratio.patch" Content-Transfer-Encoding: quoted-printable =46rom 9121254754ad742ec025b77c50d5abe3334ba547 Mon Sep 17 00:00:00 2001 =46rom: Stefano Zacchiroli Date: Mon, 26 Mar 2012 10:45:58 +0200 Subject: [PATCH 1/2] contrib: new mutt-notmuch utility for Mutt integration --- contrib/notmuch-mutt/.gitignore | 2 + contrib/notmuch-mutt/Makefile | 12 ++ contrib/notmuch-mutt/README | 59 +++++++++ contrib/notmuch-mutt/notmuch-mutt | 238 ++++++++++++++++++++++++++++++= ++++ contrib/notmuch-mutt/notmuch-mutt.rc | 9 ++ 5 files changed, 320 insertions(+) create mode 100644 contrib/notmuch-mutt/.gitignore create mode 100644 contrib/notmuch-mutt/Makefile create mode 100644 contrib/notmuch-mutt/README create mode 100755 contrib/notmuch-mutt/notmuch-mutt create mode 100644 contrib/notmuch-mutt/notmuch-mutt.rc diff --git a/contrib/notmuch-mutt/.gitignore b/contrib/notmuch-mutt/.gitign= ore new file mode 100644 index 0000000..682a577 --- /dev/null +++ b/contrib/notmuch-mutt/.gitignore @@ -0,0 +1,2 @@ +notmuch-mutt.1 +README.html diff --git a/contrib/notmuch-mutt/Makefile b/contrib/notmuch-mutt/Makefile new file mode 100644 index 0000000..87f9031 --- /dev/null +++ b/contrib/notmuch-mutt/Makefile @@ -0,0 +1,12 @@ +NAME =3D notmuch-mutt + +all: $(NAME) $(NAME).1 + +$(NAME).1: $(NAME) + pod2man $< > $@ + +README.html: README + markdown $< > $@ + +clean: + rm -f notmuch-mutt.1 README.html diff --git a/contrib/notmuch-mutt/README b/contrib/notmuch-mutt/README new file mode 100644 index 0000000..382ac91 --- /dev/null +++ b/contrib/notmuch-mutt/README @@ -0,0 +1,59 @@ +notmuch-mutt: Notmuch (of a) helper for Mutt +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +notmuch-mutt provide integration among the [Mutt] [1] mail user agent and = the +[Notmuch] [2] mail indexer. + +notmuch-mutt offer two main integration features. The first one is the abi= lity +of stating a **search query interactively** and then jump to a fresh Maild= ir +containing its search results only. The second one is the ability to +**reconstruct threads on the fly** starting from the currently highlighted +mail, which comes handy when a thread has been split across different mail= dirs, +archived, or the like. + +notmuch-mutt enables to trigger mail searches via a Mutt macro (usually F8= ) and +reconstruct threads via another (usually F9). Check the manpage for the 2-= liner +configuration snippet for your Mutt configuration files (~/.muttrc, +/etc/Muttrc, or a /etc/Muttrc.d snippet). + +A [blog style introduction] [3] to notmuch-mutt is available and includes = some +more rationale for its existence. + +Arguably, some of the logics of notmuch-mutt could disappear by adding sup= port +for a --output=3Dsymlinks flag to notmuch. + + +[1]: http://www.mutt.org/ +[2]: http://notmuchmail.org/ +[3]: http://upsilon.cc/~zack/blog/posts/2011/01/how_to_use_Notmuch_with_Mu= tt/ + + +Requirements +------------ + +To *run* notmuch-mutt you will need Perl with the following libraries: + +- Mail::Box + (Debian package: libmail-box-perl) +- Mail::Internet + (Debian package: libmailtools-perl) +- String::ShellQuote + (Debian package: libstring-shellquote-perl) +- Term::ReadLine + (Debian package: libterm-readline-gnu-perl) + +To *build* notmuch-mutt documentation you will need: + +- pod2man (coming with Perl) to generate the manpage +- markdown to generate README.html out of this file + + +License +------- + +notmuch-mutt is copyright (C) 2011-2012 Stefano Zacchiroli . + +notmuch-mutt is released under the terms of the GNU General Public License +(GPL), version 3 or above. A copy of the license is available online at +. + diff --git a/contrib/notmuch-mutt/notmuch-mutt b/contrib/notmuch-mutt/notmu= ch-mutt new file mode 100755 index 0000000..6b34e94 --- /dev/null +++ b/contrib/notmuch-mutt/notmuch-mutt @@ -0,0 +1,238 @@ +#!/usr/bin/perl -w +# +# notmuch-mutt - notmuch (of a) helper for Mutt +# +# Copyright: =A9 2011-2012 Stefano Zacchiroli =20 +# License: GNU General Public License (GPL), version 3 or above +# +# See the bottom of this file for more documentation. +# A manpage can be obtained by running "pod2man notmuch-mutt > notmuch-mut= t.1" + +use strict; +use warnings; + +use File::Path; +use Getopt::Long qw(:config no_getopt_compat); +use Mail::Internet; +use Mail::Box::Maildir; +use Pod::Usage; +use String::ShellQuote; +use Term::ReadLine; + + +my $xdg_cache_dir =3D "$ENV{HOME}/.cache"; +$xdg_cache_dir =3D $ENV{XDG_CACHE_HOME} if $ENV{XDG_CACHE_HOME}; +my $cache_dir =3D "$xdg_cache_dir/notmuch/mutt"; + + +# create an empty maildir (if missing) or empty an existing maildir" +sub empty_maildir($) { + my ($maildir) =3D (@_); + rmtree($maildir) if (-d $maildir); + my $folder =3D new Mail::Box::Maildir(folder =3D> $maildir, + create =3D> 1); + $folder->close(); +} + +# search($maildir, $query) +# search mails according to $query with notmuch; store results in $maildir +sub search($$) { + my ($maildir, $query) =3D @_; + $query =3D shell_quote($query); + + empty_maildir($maildir); + system("notmuch search --output=3Dfiles $query" + . " | sed -e 's: :\\\\ :g'" + . " | xargs --no-run-if-empty ln -s -t $maildir/cur/"); +} + +sub prompt($$) { + my ($text, $default) =3D @_; + my $query =3D ""; + my $term =3D Term::ReadLine->new( "notmuch-mutt" ); + my $histfile =3D "$cache_dir/history"; + + $term->ornaments( 0 ); + $term->unbind_key( ord( "\t" ) ); + $term->MinLine( 3 ); + $histfile =3D $ENV{MUTT_NOTMUCH_HISTFILE} if $ENV{MUTT_NOTMUCH_HISTFIL= E}; + $term->ReadHistory($histfile) if (-r $histfile); + while (1) { + chomp($query =3D $term->readline($text, $default)); + if ($query eq "?") { + system("man", "notmuch"); + } else { + $term->WriteHistory($histfile); + return $query; + } + } +} + +sub get_message_id() { + my $mail =3D Mail::Internet->new(\*STDIN); + $mail->head->get("message-id") =3D~ /^<(.*)>$/; # get message-id + return $1; +} + +sub search_action($$@) { + my ($interactive, $results_dir, @params) =3D @_; + + if (! $interactive) { + search($results_dir, join(' ', @params)); + } else { + my $query =3D prompt("search ('?' for man): ", join(' ', @params)); + if ($query ne "") { + search($results_dir,$query); + } + } +} + +sub thread_action(@) { + my ($results_dir, @params) =3D @_; + + my $mid =3D get_message_id(); + my $search_cmd =3D 'notmuch search --output=3Dthreads ' . shell_quote(= "id:$mid"); + my $tid =3D `$search_cmd`; # get thread id + chomp($tid); + + search($results_dir, $tid); +} + +sub tag_action(@) { + my $mid =3D get_message_id(); + + system("notmuch tag " + . shell_quote(join(' ', @_)) + . " id:$mid"); +} + +sub die_usage() { + my %podflags =3D ( "verbose" =3D> 1, + "exitval" =3D> 2 ); + pod2usage(%podflags); +} + +sub main() { + mkpath($cache_dir) unless (-d $cache_dir); + + my $results_dir =3D "$cache_dir/results"; + my $interactive =3D 0; + my $help_needed =3D 0; + + my $getopt =3D GetOptions( + "h|help" =3D> \$help_needed, + "o|output-dir=3Ds" =3D> \$results_dir, + "p|prompt" =3D> \$interactive); + if (! $getopt || $#ARGV < 0) { die_usage() }; + my ($action, @params) =3D ($ARGV[0], @ARGV[1..$#ARGV]); + + foreach my $param (@params) { + $param =3D~ s/folder:=3D/folder:/g; + } + + if ($help_needed) { + die_usage(); + } elsif ($action eq "search" && $#ARGV =3D=3D 0 && ! $interactive) { + print STDERR "Error: no search term provided\n\n"; + die_usage(); + } elsif ($action eq "search") { + search_action($interactive, $results_dir, @params); + } elsif ($action eq "thread") { + thread_action($results_dir, @params); + } elsif ($action eq "tag") { + tag_action(@params); + } else { + die_usage(); + } +} + +main(); + +__END__ + +=3Dhead1 NAME + +notmuch-mutt - notmuch (of a) helper for Mutt + +=3Dhead1 SYNOPSIS + +=3Dover + +=3Ditem B [I