[PATCH] doc: document notmuch new --verbose
--- doc/man1/notmuch-new.rst | 4 1 file changed, 4 insertions(+) diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst index 5eeb4926..20a1103b 100644 --- a/doc/man1/notmuch-new.rst +++ b/doc/man1/notmuch-new.rst @@ -43,6 +43,10 @@ Supported options for **new** include ``--quiet`` Do not print progress or results. +``--verbose`` +Print file names being processed. Ignored when combined with +``--quiet``. + ``--decrypt=(true|nostash|auto|false)`` If ``true``, when encountering an encrypted message, try to decrypt it while indexing, and stash any discovered session keys. -- 2.20.1 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Failure to report send errors
Dear Gnus developers, (CCing notmuch, because their users can be affected too) When message-interactive is nil, message-send can falsely report success even when sending fails and there is no way for the user to learn about the failure, expect from angry peers complaining about not receiving replies. The function responsible for this problematic behavior is message-send-mail-with-sendmail. It works in two modes: interactive and non-interactive depending on the value of message-interactive. The problem happens in the non-interactive mode (which is not the default). It executes sendmail with -odb -oem, which means that delivery should happen in background and errors should be reported to the user via email. sendmail is executed via call-process-region called without BUFFER argument, meaning that the return value is always nil. If sendmail fails to execute at all, perhaps due to corrupted configuration file, neither the message nor any error email is ever sent, but the user sees false "Sending...done" message. I think that call-process-region should be always called with a BUFFER so that sendmail exit code is returned and communicated to the user. Best regards, -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: Long delay when opening signed emails
On Tue, Jan 30 2018, Daniel Kahn Gillmor wrote: > Hi Michal-- > > On Tue 2018-01-30 17:17:54 +0100, Michal Sojka wrote: >> Hi all, >> >> I experience annoyingly long delay, when opening some signed emails in >> Emacs. This is likely related to the following lines appearing in my >> log when opening the email: >> >> Jan 30 17:07:46 dirmngr[7526]: no CRL available for issuer id >> A401B7A860C859FEA90E1A7EEE2BAF37C7FB918F >> Jan 30 17:08:06 dirmngr[7526]: resolving 'crl3.digicert.com' failed: Server >> indicated a failure >> Jan 30 17:08:06 dirmngr[7526]: can't connect to 'crl3.digicert.com': host >> not found >> Jan 30 17:08:06 dirmngr[7526]: error retrieving >> 'http://crl3.digicert.com/TERENAeSciencePersonalCA3.crl': Server indicated a >> failure >> Jan 30 17:08:06 dirmngr[7526]: crl_fetch via DP failed: Server indicated a >> failure >> Jan 30 17:08:06 dirmngr[7526]: command 'ISVALID' failed: Server indicated a >> failure >> >> I don't understand why resolving crl3.digicert.com fails, because it >> works from command line. > > I think the e-mail in question is S/MIME-signed. is that right? Yes, that's correct. > It looks like dirmngr is having some problems with network connectivity > -- perhaps it has the wrong information about DNS resolvers? > > as a workaround, have you tried terminating dirmngr to let it restart > when needed? you can do that with: > > gpgconf --kill dirmngr > > (it should respawn automatically as needed) That didn't help. >> Any suggestions how to solve the failure or at least to get rid of the >> delay? > > Apart from the workaround described above, if you decide that you'd > rather avoid doing CRL checks in general (you might want that to avoid > metadata leakage at least), you could put "disable-crl-checks" on its > own line in ~/.gnupg/gpgsm.conf Perfect, that prevents the delays. > See also https://dev.gnupg.org/T3348 -- i'm asking upstream to default > to False there. Hmm, now I see that my problem is probably the same as in https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=842291 referenced from your GPG bug report. Thank you. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Long delay when opening signed emails
Hi all, I experience annoyingly long delay, when opening some signed emails in Emacs. This is likely related to the following lines appearing in my log when opening the email: Jan 30 17:07:46 dirmngr[7526]: no CRL available for issuer id A401B7A860C859FEA90E1A7EEE2BAF37C7FB918F Jan 30 17:08:06 dirmngr[7526]: resolving 'crl3.digicert.com' failed: Server indicated a failure Jan 30 17:08:06 dirmngr[7526]: can't connect to 'crl3.digicert.com': host not found Jan 30 17:08:06 dirmngr[7526]: error retrieving 'http://crl3.digicert.com/TERENAeSciencePersonalCA3.crl': Server indicated a failure Jan 30 17:08:06 dirmngr[7526]: crl_fetch via DP failed: Server indicated a failure Jan 30 17:08:06 dirmngr[7526]: command 'ISVALID' failed: Server indicated a failure I don't understand why resolving crl3.digicert.com fails, because it works from command line. $ host crl3.digicert.com crl3.digicert.com is an alias for cs9.wac.phicdn.net. cs9.wac.phicdn.net has address 93.184.220.29 Any suggestions how to solve the failure or at least to get rid of the delay? Thanks -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: address completion, allow sender/recipient and filters
On Wed, May 18 2016, Mark Walters wrote: > This commit lets the user customize the address completion. It makes > two changes. Thanks Mark, now it LGTM. I configured my address completion to be based on received emails and I'm surprised how many variations of my email address is used by spammers :) Cheers -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v3] emacs: address completion, allow sender/recipient and filters
Hi Mark, few minor comments bellow. On Mon, May 16 2016, Mark Walters wrote: > This commit lets the user customize the address completion. Add "It makes two changes." > The first change controls whether to build the address completion list > based on messages you have sent or you have received (the latter is > much faster). > > The second change add a possible filter query to limit the messages > used -- for example, setting this to date:1y.. would limit the > address completions to addresses used in the last year. This speeds up > the address harvest and may also make the search less cluttered as old > addresses may well no longer be valid. > --- > > This version uses the docstrings suggested my Michal (which are much > better than mine), and renames some variables, as he suggested, to > make the code clearer. > > I wondered about allowing the user to specify completion based on > message "sent or received", rather than either sent, or received, but > think that is adding too much mess. We could also allow completion > based on any messages in the database, which would the include > completion based on messages received via mailing lists or > distribution lists. > > I also note that if the user enters a bad query into the filter query > box (eg 6M.. rather than date:6M..) they may get an obscure error as > notmuch/xapian fails. I don't see a goo way round that so have left > that as a "don't do that" case. Agreed. > emacs/notmuch-address.el | 119 > --- > emacs/notmuch-company.el | 2 +- > 2 files changed, 82 insertions(+), 39 deletions(-) > > diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el > index aafbe5f..3dc5da9 100644 > --- a/emacs/notmuch-address.el > +++ b/emacs/notmuch-address.el > @@ -28,15 +28,51 @@ > ;; > (declare-function company-manual-begin "company") > > -(defcustom notmuch-address-command 'internal > - "The command which generates possible addresses. It must take a > -single argument and output a list of possible matches, one per > -line. The default value of `internal' uses built-in address > -completion." > +(defvar notmuch-address-last-harvest 0 > + "Time of last address harvest") > + > +(defvar notmuch-address-completions (make-hash-table :test 'equal) > + "Hash of email addresses for completion during email composition. > + This variable is set by calling `notmuch-address-harvest'.") > + > +(defvar notmuch-address-full-harvest-finished nil > + "t indicates that full completion address harvesting has been > +finished") > + > +(defcustom notmuch-address-command '(sent nil) > + "Determines how to generate address completion candidates. > + > +If it is a string then that string should be an external program > +which must take a single argument (searched string) and output a > +list of completion candidates, one per line. > + > +Alternatively, it can be a (non-nil) list, in which case internal > +completion is used; in this case the list should have form > +'(DIRECTION FILTER), where DIRECTION is either sent or received > +and specifies whether the candidates are searched in messages > +sent by the user or received by the user (note received by is > +much faster), and FILTER is either nil or a filter-string, such > +as \"date:1y..\" to append to the query. > + > +If this variable is nil then address completion is disabled." >:type '(radio > - (const :tag "Use internal address completion" internal) > + (list :tag "Use internal address completion" > + (radio > + :tag "Base completion on messages you have" > + :value sent > + (const :tag "sent" sent) > + (const :tag "received" received)) I think, users will be more happy if they understand the difference without reading the full doc string. (const :tag "sent (more accurate)" sent) (const :tag "received (faster)" received)) Thanks. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: message-default-mail-headers not working in notmuch 0.22
On Fri, Apr 29 2016, David Edmondson wrote: > On Fri, Apr 29 2016, Sebastian Fischmeister wrote: >> After upgrading to notmuch 0.22, my emacs config seems broken: >> >> (setq message-default-mail-headers "Reply-to: m...@example.com\nBcc: >> m...@example.com") >> >> When creating a new mail, it has no header other than "To:" and >> "Subject:". >> >> Since I cannot find any item in the NEWS related to this release, is >> this expected behaviour? > > No, it's not intended. Please try this patch: > > diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el > index 0445975..399e138 100644 > --- a/emacs/notmuch-mua.el > +++ b/emacs/notmuch-mua.el > @@ -338,7 +338,10 @@ modified. This function is notmuch addaptation of > ;; We need to convert any string input, eg from rmail-start-mail. > (dolist (h other-headers other-headers) > (if (stringp (car h)) (setcar h (intern (capitalize (car h > - (args (list yank-action send-actions))) > + (args (list yank-action send-actions)) > + ;; Cause `message-setup-1' to do things relevant for mail, > + ;; such as observe `message-default-mail-headers'. > + (message-this-is-mail t)) Yes, this seems to be reasonable thing to do. Thanks. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: Mail merge with notmuch
Hi Sebastian, On Sat, Jan 16 2016, Sebastian Fischmeister wrote: > Hi, > > Is there a straightforward way to for mail merge with notmuch? I need to > send emails with only minor modifications to a number of people. If I > could send mails from the command line, then that would be perfect. > > Any ideas? You can do this easily without notmuch. You need a locally configured MTA such as sendmail or exim (which provides sendmail alias) and then run something like: cat people | while read EMAIL NAME; do sendmail -t < To: $EMAIL Subject: Foobar Hello $NAME! ... EOF done BR -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v6] devel/emacs: add devel/try-emacs-mua
On Wed, Jan 06 2016, Tomi Ollila wrote: > devel/try-emacs-mua provides an easy way to try and experiment > with the notmuch emacs client distributed in emacs subdirectory of > the notmuch source tree. LGTM. I tested it with various use cases I have it my mind and it works as I expect. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 1/3] emacs: Fix mail composition under Emacs 23
On Tue, Jan 05 2016, David Bremner wrote: > Michal Sojka <sojk...@fel.cvut.cz> writes: > >> +(when return-action (nconc args '(return-action))) >> +(apply 'message-setup-1 >> + ;; The following sexp is copied from `message-mail' > >> + (nconc >> +`((To . ,(or to "")) (Subject . ,(or subject ""))) > > I missed this the first time, but I a bit worried about this used of > nconc. It seems to fall under "A common pitfall is to use a quoted > constant list as a non-last argument to ‘nconc’" (from the elisp > manual). In any case it's not really performance critical code (I > guess?) so maybe we could just use append? Yes, this make sense. As the nconc is already in master, I'll not combine the fix for this with 1/3, but I'm sending a separate patch that applies after 3/3. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v5] devel/emacs: add devel/try-emacs-mua
Hi Tomi, this looks almost good. A few minor comments below. On Mon, Jan 04 2016, Tomi Ollila wrote: > devel/try-emacs-mua provides an easy way to try and experiment > with the notmuch emacs client distributed in emacs subdirectory of > the notmuch source tree. > > try-emacs-mua starts a new emacs process and if initial checks pass > *scratch* buffer is filled with information of how to begin. > > Normal emacs command line arguments can be used, like -q or -Q. > These arguments are appended verbatim to the starting emacs process. > > If the emacs version in use is smaller than 24.4, special care is taken > to ensure that notmuch*.elc files older than corresponding .el files > are not loaded. Since emacs 24.4, setting `load-prefer-newer' variable > takes care of this. > --- > > Thanks to Michal for review of v4 > id:1450610032-23776-1-git-send-email-tomi.oll...@iki.fi > in id:87vb7aco77@steelpick.2x.cz > > This should address the issues: > > 1) no more command line filtering > 2) dropped (outcommented) file-truename advising > 3) added information which notmuch binary is used > 4) dropped popping messages buffer (fixing 2 issues) > > Added: > * code to load ~/.emacs.d/notmuch-config.el if exists and not loaded already > * pop messages buffer if (require 'notmuch) fails > * don't suggest package-initialize if emacs version is 23 > > > devel/try-emacs-mua | 156 > > 1 file changed, 156 insertions(+) > create mode 100755 devel/try-emacs-mua > > diff --git a/devel/try-emacs-mua b/devel/try-emacs-mua > new file mode 100755 > index ..0236b438c30a > --- /dev/null > +++ b/devel/try-emacs-mua > @@ -0,0 +1,156 @@ > +#!/bin/sh > +:; set -x; exec "${EMACS:-emacs}" --debug-init --load "$0" "$@"; exit > +;; > +;; Try the notmuch emacs client located in ../notmuch/emacs directory > +;; > +;; Run this without arguments; emacs window opens with some usage information > +;; > +;; Authors: Tomi Ollila> +;; > +;; http://www.emacswiki.org/emacs/EmacsScripts was a useful starting point... > +;; > +;; Licence: GPLv3+ > +;; > + > +(message "Starting '%s'" load-file-name) > + > +(set-buffer "*scratch*") > + > +(setq initial-buffer-choice t) ;; *scratch* buffer After initial tuning of emacs configuration according to the hints in the scratch buffer, people might want to automate things. For example something like: devel/try-emacs-mua -q -f notmuch-hello This does not work as one would expect, because the notmuch-hello buffer is buried under the *scratch* buffer. To get the hello screen after the start one has to run: devel/try-emacs-mua -q -f notmuch-hello --eval '(setq initial-buffer-choice nil)' This could be also documented in the scratch buffer. Or it might be easier, in this case, to run emacs without this script at all. There will be no logging, but this might be OK for some people. Actually I don't know who are you writing this script for? For notmuch developers or for users who want to debug or test development version? > + > +(when (featurep 'notmuch) > + (insert " > +Notmuch has been loaded to this emacs (during processing of the init file) > +which means it is (most probably) loaded from different source than expected. > + > +Please run \"" (file-name-nondirectory load-file-name) > +"\" with '-q' (or '-Q') as an argument, to disable > +processing of the init file -- you can load it after emacs has started\n > +exit emacs (y or n)? ") > + (if (y-or-n-p "exit emacs") > + (kill-emacs) > +(error "Stopped reading %s" load-file-name))) > + > +(let ((pdir (file-name-directory > + (directory-file-name (file-name-directory load-file-name) > + (unless (file-exists-p (concat pdir "emacs/notmuch-lib.el")) > +(insert "Cannot find notmuch-emacs source directory > +while looking at: " pdir "emacs\n\nexit emacs (y or n)? ") > +(if (y-or-n-p "exit emacs") > + (kill-emacs) > + (error "Stopped reading %s" load-file-name))) > + (setq try-notmuch-source-directory (directory-file-name pdir)) > + (setq try-notmuch-emacs-directory (concat pdir "emacs/")) > + (setq load-path (cons try-notmuch-emacs-directory load-path))) > + > +;; they say advice doesn't work for primitives (functions from c source) > +;; well, these 'before' advice works for emacs 23.1 - 24.5 (at least) > +;; ...and for our purposes 24.3 is enough (there is no load-prefer-newer > there) > +;; note also that the old, "obsolete" defadvice mechanism was used, but that > +;; is the only one available for emacs 23 and 24 up to 24.3. > + > +(if (boundp 'load-prefer-newer) > +(defadvice require (before before-require activate) > + (unless (featurep feature) > + (message "require: %s" feature))) > + ;; else: special require "short-circuit"; after load feature is provided... > + ;; ... in notmuch sources we always use require and there are no loops > + (defadvice require (before before-require activate) > +
Re: [PATCH] devel/emacs: add devel/try-emacs-mua.sh
On Thu, Nov 12 2015, Mark Walters wrote: >> devel/try-emacs-mua.sh provides an easy way to try and experiment with >> the notmuch emacs client provided in emacs subdirectory of notmuch >> source tree. [...] > > I like this -- a nice simple way to get to a working but uncustomised > test environment. > > I don't know how easy the following would be but one addition that I > would find very useful would be a way to make this use the test > corpus. This would be useful for giving a completely standard > environment for interactive testing, but also for debugging what is > going on when tests fail. This is already possible. If a test fails, its working directory is not deleted and you can run e.g. ./test/tmp.T310-emacs/run_emacs, where you can interactively reproduce the test. What you miss compared to Tomi's approach is the hint screen and extra logging. BR -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v4] devel/emacs: add devel/try-emacs-mua
Hi Tomi, On Sun, Dec 20 2015, Tomi Ollila wrote: > devel/try-emacs-mua provides an easy way to try and experiment > with the notmuch emacs client distributed in emacs subdirectory of > the notmuch source tree. > > try-emacs-mua starts a new emacs process and if initial checks pass > *scratch* buffer is filled with information of how to begin. > > User can add normal emacs flags, like -q, -Q and e.g. -f notmuch. Any > given plain filename arguments are filtered out (with message). Why are plain files filtered? Yesterday, when I experimented with jl-encrypt.el, I started emacs like: emacs -Q --directory ... --load notmuch.el /tmp/jl-encrypt.el This allowed me to experiment with it (edebug), modify it and restart emacs sessions easily without the need to type C-x C-f and the file name in every session. > If the emacs version in use is smaller than 24.4, special care is taken > to ensure that notmuch*.elc files older than corresponding .el files > are not loaded. Since emacs 24.4, setting `load-prefer-newer' variable > takes care of this. > --- > > Now all-elisp, vs v3: id:1447457397-11688-1-git-send-email-tomi.oll...@iki.fi > > In id:87si4bwrsu@qmul.ac.uk Mark had some thoughts if something from this > could perhaps be used as some idea how to improve test testing. We'll have > to think more of that later. Rest of the review is pretty much irrelevant > for this (But I fixed that particular bug before turning to this ;). > > devel/try-emacs-mua | 153 > > 1 file changed, 153 insertions(+) > create mode 100755 devel/try-emacs-mua > > diff --git a/devel/try-emacs-mua b/devel/try-emacs-mua > new file mode 100755 > index ..1f916d2806f9 > --- /dev/null > +++ b/devel/try-emacs-mua > @@ -0,0 +1,153 @@ > +#!/bin/sh > +:; set -x; exec "${EMACS:-emacs}" --debug-init --load "$0" "$@"; exit Huh! Nice trick, especially the ":;" :) > +;; > +;; Try the notmuch emacs client located in ../notmuch/emacs directory > +;; > +;; Run this without arguments; emacs window opens with some usage information > +;; > +;; Authors: Tomi Ollila> +;; > +;; http://www.emacswiki.org/emacs/EmacsScripts was a useful starting point... > +;; > +;; Licence: GPLv3+ > +;; > + > +(message "Starting '%s'" load-file-name) > + > +(set-buffer "*scratch*") > + > +(setq initial-buffer-choice t) ;; *scratch* buffer > + > +(when (featurep 'notmuch) > + (insert " > +Notmuch has been loaded to this emacs (during processing of the init file) > +which means it is (most probably) loaded from different source than expected. > + > +Please run \"" (file-name-nondirectory load-file-name) > +"\" with '-q' (or '-Q') as an argument, to disable > +processing of the init file -- you can load it after emacs has started\n > +exit emacs (y or n)? ") > + (if (y-or-n-p "exit emacs") > + (kill-emacs) > +(error "Stopped reading %s" load-file-name))) > + > +(let ((pdir (file-name-directory > + (directory-file-name (file-name-directory load-file-name) > + (unless (file-exists-p (concat pdir "emacs/notmuch-lib.el")) > +(insert "Cannot find notmuch-emacs source directory > +while looking at: " pdir "emacs\n\nexit emacs (y or n)? ") > +(if (y-or-n-p "exit emacs") > + (kill-emacs) > + (error "Stopped reading %s" load-file-name))) > + (setq try-notmuch-source-directory (directory-file-name pdir)) > + (setq try-notmuch-emacs-directory (concat pdir "emacs/")) > + (setq load-path (cons try-notmuch-emacs-directory load-path))) > + > +;; for logging, debugging and load tracing (broke on emacs 24.5.1 :O) > +;;(defadvice file-truename (before before-file-truename activate) > + (message "file-truename: '%s' '%s' '%s'" filename counter prev-dirs)) > +;; (unless counter > +;;(message "file-truename: %s" filename))) What is the benefit of advising file-truename? Especially when you do other type of logging below. I'd prefer to have this as a comment in the file. Or is this some leftover from your experiments? > + > +;; they say advice doesn't work for primitives (functions from c source) > +;; well, these 'before' advice works for emacs 23.1 - 24.5 (at least) > +;; ...and for our purposes 24.3 is enough (there is no load-prefer-newer > there) > +;; note also that the old, "obsolete" defadvice mechanism was used, but that > +;; is the only one available for emacs 23 and 24 up to 24.3. > + > +(if (boundp 'load-prefer-newer) > +(defadvice require (before before-require activate) > + (unless (featurep feature) > + (message "require: %s" feature))) > + ;; else: special require "short-circuit"; after load feature is provided... > + ;; ... in notmuch sources we always use require and there are no loops > + (defadvice require (before before-require activate) > +(unless (featurep feature) > + (message "require: %s" feature) > + (let ((name (symbol-name feature))) > + (if (and (string-match "^notmuch" name) > +
[PATCH 2/3] emacs: Refactor notmuch-mua-mail
This should be more readable. --- emacs/notmuch-mua.el | 26 +- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index d4950cb..2d6825d 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -320,24 +320,24 @@ modified. This function is notmuch addaptation of (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)) - (let ((args (list yank-action send-actions))) + (let ((headers +;; The following sexp is copied from `message-mail' +(nconc + `((To . ,(or to "")) (Subject . ,(or subject ""))) + ;; C-h f compose-mail says that headers should be specified as + ;; (string . value); however all the rest of message expects + ;; headers to be symbols, not strings (eg message-header-format-alist). + ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html + ;; We need to convert any string input, eg from rmail-start-mail. + (dolist (h other-headers other-headers) + (if (stringp (car h)) (setcar h (intern (capitalize (car h + (args (list yank-action send-actions))) ;; message-setup-1 in Emacs 23 does not accept return-action ;; argument. Pass it only if it is supplied by the caller. This ;; will never be the case when we're called by `compose-mail' in ;; Emacs 23. (when return-action (nconc args '(return-action))) -(apply 'message-setup-1 - ;; The following sexp is copied from `message-mail' - (nconc - `((To . ,(or to "")) (Subject . ,(or subject ""))) - ;; C-h f compose-mail says that headers should be specified as - ;; (string . value); however all the rest of message expects - ;; headers to be symbols, not strings (eg message-header-format-alist). - ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html - ;; We need to convert any string input, eg from rmail-start-mail. - (dolist (h other-headers other-headers) - (if (stringp (car h)) (setcar h (intern (capitalize (car h))) - args)) +(apply 'message-setup-1 headers args)) (notmuch-fcc-header-setup) (message-sort-headers) (message-hide-headers) -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 3/3] emacs: Handle switch-function argument of notmuch-mua-mail
notmuch-mua-mail ignored the switch-function argument and always used the function returned by notmuch-mua-get-switch-function instead. In order to support standard emacs interfaces (compose-mail in this case), this commit changes notmuch-mua-mail to use the switch-function argument if it is non-nil and notmuch-mua-get-switch-function otherwise. --- emacs/notmuch-mua.el | 9 + 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 2d6825d..45a6daa 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -278,7 +278,7 @@ Note that these functions use `mail-citation-hook' if that is non-nil." (define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send) -(defun notmuch-mua-pop-to-buffer (name) +(defun notmuch-mua-pop-to-buffer (name switch-function) "Pop to buffer NAME, and warn if it already exists and is modified. This function is notmuch addaptation of `message-pop-to-buffer'." @@ -291,7 +291,7 @@ modified. This function is notmuch addaptation of (progn (gnus-select-frame-set-input-focus (window-frame window)) (select-window window)) - (funcall (notmuch-mua-get-switch-function) buffer) + (funcall switch-function buffer) (set-buffer buffer)) (when (and (buffer-modified-p) (not (prog1 @@ -299,7 +299,7 @@ modified. This function is notmuch addaptation of "Message already being composed; erase? ") (message nil (error "Message being composed"))) - (funcall (notmuch-mua-get-switch-function) name) + (funcall switch-function name) (set-buffer name)) (erase-buffer) (notmuch-message-mode))) @@ -319,7 +319,8 @@ modified. This function is notmuch addaptation of (push (cons 'From (concat (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) - (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)) + (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to) +(or switch-function (notmuch-mua-get-switch-function))) (let ((headers ;; The following sexp is copied from `message-mail' (nconc -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
notmuch-message-mode initialization follow-up
Hi, the first patch fixes the problem reported in id:87wprt2r4f.fsf@zancas.localnet. Patch 2/3 is just refactoring. Patch 3/3 is reaction to id:87y4cc3qse.fsf@zancas.localnet; I'm not sure whether it addresses exactly what David had in mind, but IMHO it is an improvement. Cheers. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 1/3] emacs: Fix mail composition under Emacs 23
Commit 570c0aeb40bd0c3af8174624a55e968f62c44f09 reworked notmuch-mua-mail function in a way that worked only under Emacs 24. The reason was that message-setup-1 took one argument less in Emacs 23. We fix this by only supplying the return-action argument when it is actually set by the caller. --- emacs/notmuch-mua.el | 30 ++ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index a66a306..d4950cb 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -320,18 +320,24 @@ modified. This function is notmuch addaptation of (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)) - (message-setup-1 - ;; The following sexp is copied from `message-mail' - (nconc -`((To . ,(or to "")) (Subject . ,(or subject ""))) -;; C-h f compose-mail says that headers should be specified as -;; (string . value); however all the rest of message expects -;; headers to be symbols, not strings (eg message-header-format-alist). -;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html -;; We need to convert any string input, eg from rmail-start-mail. -(dolist (h other-headers other-headers) - (if (stringp (car h)) (setcar h (intern (capitalize (car h))) - yank-action send-actions return-action) + (let ((args (list yank-action send-actions))) +;; message-setup-1 in Emacs 23 does not accept return-action +;; argument. Pass it only if it is supplied by the caller. This +;; will never be the case when we're called by `compose-mail' in +;; Emacs 23. +(when return-action (nconc args '(return-action))) +(apply 'message-setup-1 + ;; The following sexp is copied from `message-mail' + (nconc + `((To . ,(or to "")) (Subject . ,(or subject ""))) + ;; C-h f compose-mail says that headers should be specified as + ;; (string . value); however all the rest of message expects + ;; headers to be symbols, not strings (eg message-header-format-alist). + ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html + ;; We need to convert any string input, eg from rmail-start-mail. + (dolist (h other-headers other-headers) + (if (stringp (car h)) (setcar h (intern (capitalize (car h))) + args)) (notmuch-fcc-header-setup) (message-sort-headers) (message-hide-headers) -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2] emacs: Improve notmuch-message-mode initialization
On Fri, Jan 01 2016, David Bremner wrote: > David Bremner <da...@tethera.net> writes: > >> Michal Sojka <sojk...@fel.cvut.cz> writes: >> >>> This commit fixes these problems by replacing a call to message-mail >>> with notmuch-specific code that is (hopefully) equivalent to >>> message-mail functionality before introduction of >>> notmuch-message-mode. >> >> pushed to master > > It seems to have broken something with emacs23 > >https://travis-ci.org/notmuch/notmuch/builds/99715812 > > I just verified the same problems (more or less) exist in debian > oldstable. A fixup patch was sent as id:1451753265-26713-2-git-send-email-sojk...@fel.cvut.cz. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: notmuch-mua and jl-encrypt (was: file-error "not a regular file")
Hi fauno, On Wed, Dec 30 2015, fauno wrote: > Michal Sojka <sojk...@fel.cvut.cz> writes: >> can you share more details about how do you use jl-encrypt and which >> functions do not work? Recently, I posted a few patches that fix some >> problems related to the replacing message-mode with >> notmuch-message-mode. Maybe, there is more what we can do to not break >> user's setups. > > i've been using jl-encrypt unmodified with notmuch for a while now. i > just require it on my .emacs > > since 0.21, i had to rebind notmuch-message-mode C-c C-c and C-s C-s > keys: > > (define-key notmuch-message-mode-map (kbd "C-c C-c") > 'jl-message-send-and-exit) > (define-key notmuch-message-mode-map (kbd "C-c C-s") 'jl-message-send) > > and solved the issue of fcc by setting notmuch-fcc-dirs to nil and > making my mta send me bcc of my own email. > > this has worked correctly for the last week. > >> From a brief look at jl-encrypt, it seems it is tightly bound to gnus, >> because it uses gnus-message-setup-hook. Maybe it will work again with >> notmuch if you use my patch [1] and run >> >> (add-hook 'message-setup-hook 'jl-encrypt-if-possible) > > i applied your patch to the 0.21 release and byte-compiled notmuch-mua.el > > the message is sent unencrypted unless i rebind C-c C-c as before. the > email is encrypted but it asks for the recipient, which i'm guessing > emacs can't figure out for itself anymore? > > also tested with `emacs -q` and loading this file, and jl-encrypt never > asks to encrypt the email when possible: > > # /tmp/emacs > > (require 'notmuch) > (add-to-list 'load-path "~/.emacs.d/lisp/") > (require 'jl-encrypt) > (add-hook 'message-setup-hook 'jl-encrypt-if-possible) > (add-hook 'message-setup-hook 'mml-secure-message-sign-pgpmime) > > by adding the C-c C-c rebind, it gets to encrypt, but asks for recipient > again. I looked into this and I think that the following workaround could work for you: (defun jl-notmuch-message-send-and-exit ( arg) (interactive "P") (let ((message-fcc-handler-function #'notmuch-fcc-handler)) (jl-message-send-and-exit arg))) (defun jl-notmuch-message-send ( arg) (interactive "P") (let ((message-fcc-handler-function #'notmuch-fcc-handler)) (jl-message-send arg))) (define-key notmuch-message-mode-map (kbd "C-c C-c") 'jl-notmuch-message-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") 'jl-notmuch-message-send) Unfortunately, I couldn't find a less intrusive solution for now, because notmuch uses the same "trick" (remapping keyboard shortcuts) as jl-encrypt to override message sending from message.el. This means that either jl-encrypt or notmuch can be bound to these keys but not both. Notmuch has to use this mechanism until the bug [1] is fixed in Emacs. Another possibility would be to introduce something like notmuch-send-hook which would allow other packages to hook into notmuch sending process, but this could be even more complex that the above workaround, so I don't think it is worth the effort. Best regards, -Michal [1] http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21174 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: file-error "not a regular file"
Hi fauno, On Tue, Dec 29 2015, fauno wrote: > David Bremnerwrites: > >> I guess the tl;dr is that I can't duplicate this problem. Looking at the >> traceback Peter provided, it looks like he is using >> "send-message-without-bullets" to send the message. Since this isn't a >> notmuch function, it's likely bypassing the notmuch fcc setup that >> notmuch-mua-send and notmuch-mua-send-and-exit do. > > i've found the issue. i'm using jl-encrypt that expects email written > in message-mode, and since notmuch is now a minor mode some functions > that were expected to be there didn't work anymore. can you share more details about how do you use jl-encrypt and which functions do not work? Recently, I posted a few patches that fix some problems related to the replacing message-mode with notmuch-message-mode. Maybe, there is more what we can do to not break user's setups. From a brief look at jl-encrypt, it seems it is tightly bound to gnus, because it uses gnus-message-setup-hook. Maybe it will work again with notmuch if you use my patch [1] and run (add-hook 'message-setup-hook 'jl-encrypt-if-possible) -Michal [1] id:1450690875-2111-2-git-send-email-sojk...@fel.cvut.cz ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] doc/reply: Clarify how reply-to header is handled
Current documentation and comments in the code do not correspond to the actual code and tests in the test suite ("Un-munging Reply-To" in T230-reply-to-sender.sh). Fix it. --- doc/man1/notmuch-reply.rst | 6 -- notmuch-reply.c| 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/man1/notmuch-reply.rst b/doc/man1/notmuch-reply.rst index cfbd4ea..d73f8f1 100644 --- a/doc/man1/notmuch-reply.rst +++ b/doc/man1/notmuch-reply.rst @@ -13,8 +13,10 @@ DESCRIPTION Constructs a reply template for a set of messages. To make replying to email easier, **notmuch reply** takes an existing -set of messages and constructs a suitable mail template. The Reply-to: -header (if any, otherwise From:) is used for the To: address. Unless +set of messages and constructs a suitable mail template. Its To: +address is set according to the original email in this way: if the +Reply-to: header is present and different from any To:/Cc: address it +is used, otherwise From: header is used. Unless ``--reply-to=sender`` is specified, values from the To: and Cc: headers are copied, but not including any of the current user's email addresses (as configured in primary\_mail or other\_email in the .notmuch-config diff --git a/notmuch-reply.c b/notmuch-reply.c index 6df54fc..3c6d685 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -332,7 +332,7 @@ add_recipients_from_message (GMimeMessage *reply, * field and use the From header. This ensures the original sender * will get the reply even if not subscribed to the list. Note * that the address in the Reply-To header will always appear in - * the reply. + * the reply if reply_all is true. */ if (reply_to_header_is_redundant (message)) { reply_to_map[0].header = "from"; -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: notmuch-reply doesn't use Reply-To
Hi all, On Fri, Dec 04 2015, Jani Nikula wrote: > On Fri, 04 Dec 2015, Damien Cassouwrote: >> David Bremner writes: >> >>> Damien Cassou writes: >>> "To" : "r...@inria.fr", "Reply-To" : "r...@inria.fr", "From" : "seas...@rmod.inria.fr", "Subject" : "[rmod] [Mm10s] 2015-11-30", "Date" : "Mon, 30 Nov 2015 07:00:01 +0100" >>> >>> A quick look at the code suggests this is falling victim to the >>> "reply-to munging" detection code, which considers a reply-to field >>> redudant if it duplicates one of the other fields. From the source >>> >>> /* Some mailing lists munge the Reply-To header despite it being A Bad >>> * Thing, see http://www.unicom.com/pw/reply-to-harmful.html >>> * >>> * The munging is easy to detect, because it results in a >>> * redundant reply-to header, (with an address that already exists >>> * in either To or Cc). So in this case, we ignore the Reply-To >>> * field and use the From header. This ensures the original sender >>> * will get the reply even if not subscribed to the list. Note >>> * that the address in the Reply-To header will always appear in >>> * the reply. >>> */ >> >> >> The last sentence seems to contradict my example: >> >> Note that the address in the Reply-To header will always appear in >> the reply. >> >> Here is the reply message, and it does not contain the address in Reply-To. > > This was true way back when notmuch reply only knew about reply all. For > --reply-to=sender, it's broken. The simplest "fix" might be I don't think that this is broken for two reasons: 1. In tests/T230-reply-to-sender.sh, there is "Un-munging Reply-To" test, which checks the same combination of headers as in Damien's case and uses --reply-to=sender. The test passes and the reply has To=From. 2. When replying to mailing lists using reply-to munging, current notmuch behavior allows me to decide whether to reply 1) privately to the mail sender (--reply-to=sender) or 2) to the mailing list (--reply-to=all). The proposed change would make option 1) harder. Therefore I suggest to fix this by applying the documentation patch from the follow-up mail. -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2] test: Unset ALTERNATE_EDITOR before running emacsclient
ALTERNATE_EDITOR causes emacsclient to run an alternate editor if the emacs server is not ready. This can collide with intended functionality in test-lib.sh. If the ALTERNATE_EDITOR is set but empty, emacsclient runs emacs daemon and tries to connect to it. When this happens the emacs run by test-lib.sh fails to start the server and the subsequent attempts to use the server fail because the daemon started by emacsclient does not know about notmuch-test-progn. This leads to test suite failure due to time out on any emacs test. --- test/test-lib.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test-lib.sh b/test/test-lib.sh index 126911f..270c718 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -82,6 +82,9 @@ unset CDPATH unset GREP_OPTIONS +# For emacsclient +unset ALTERNATE_EDITOR + # Convenience # # A regexp to match 5 and 40 hexdigits -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] test: Always use paths without symlinks
When notmuch sources are at a symlinked path, some tests fail because one part of the test uses physical path and another uses logical path (with symlinks). For example the following test output is produced when the test is started from /home/src/symlink-to-notmuch, which is a symlink to /home/src/notmuch. FAIL notmuch-fcc-dirs set to a string --- T310-emacs.26.OUTPUT2015-12-29 08:54:29.055878637 + +++ T310-emacs.26.EXPECTED 2015-12-29 08:54:29.055878637 + @@ -1,5 +1,5 @@ From: Notmuch Test SuiteTo: Subject: -Fcc: /home/src/notmuch/test/tmp.T310-emacs/mail/sent-string +Fcc: /home/src/symlink-to-notmuch/test/tmp.T310-emacs/mail/sent-string --text follows this line-- nil This commit makes all paths in test scripts physical. With it, all tests pass even when run from a symlinked directory. --- test/test-lib-common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh index 5eb618c..4e17b78 100644 --- a/test/test-lib-common.sh +++ b/test/test-lib-common.sh @@ -48,7 +48,7 @@ restore_database () { # Test the binaries we have just built. The tests are kept in # test/ subdirectory and are run in 'trash directory' subdirectory. -TEST_DIRECTORY=$(pwd) +TEST_DIRECTORY=$(pwd -P) notmuch_path=`find_notmuch_path "$TEST_DIRECTORY"` # configure output -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] test: Unset ALTERNATE_EDITOR before running emacsclient
ALTERNATE_EDITOR causes emacsclient to run an alternate editor if the emacs server is not ready. This can collide with intended functionality in test-lib.sh. If the ALTERNATE_EDITOR is set but empty, emacsclient runs emacs daemon and tries to connect to it. When this happens the emacs run by test-lib.sh fails to start the server and the subsequent attempts to use the server fail because the daemon started by emacsclient does not know about notmuch-test-progn. This leads to test suite failure due to time out on any emacs test. --- test/test-lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-lib.sh b/test/test-lib.sh index 126911f..0f6a6cf 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1152,6 +1152,7 @@ test_emacs () { rm -f OUTPUT touch OUTPUT + unset ALTERNATE_EDITOR ${TEST_EMACSCLIENT} --socket-name="$EMACS_SERVER" --eval "(notmuch-test-progn $@)" } -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: address completion, allow sender/recipient and filters
Hi Mark, I tried this patch. When I set the variable to 'received, first completion candidates are generated incorrectly. The query looked as "(to:sojk...@fel.cvut.cz) and (to:prefix*)". It should be "... (from:prefix*)". This "to:" comes from notmuch-address-options or from notmuch-company so these should be changes as well. Other minor comments are below. Thanks. -Michal On Sun, Dec 20 2015, Mark Walters wrote: > This commit lets the user customize the address completion. > > The first change controls whether to build the address completion list > based on messages you have sent or you have received (the latter is > much faster). > > The second change add a possible filter query to limit the messages > used -- for example, setting this to date:1y.. would limit the > address completions to addresses used in the last year. This speeds up > the address harvest and may also make the search less cluttered as old > addresses may well no longer be valid. > --- > > There was some discussion on irc about the address completion harvest > taking a long time. This patch makes is possible to tune this > behaviour. It may be that this complicates the customize variable too > much -- but if nothing else its a patch that anyone who experiences > problems can try. > > Best wishes > > Mark > > emacs/notmuch-address.el | 64 +--- > 1 file changed, 44 insertions(+), 20 deletions(-) > > diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el > index 49e2402..8942fe4 100644 > --- a/emacs/notmuch-address.el > +++ b/emacs/notmuch-address.el > @@ -26,15 +26,40 @@ > ;; > (declare-function company-manual-begin "company") > > +(defvar notmuch-address-last-harvest 0 > + "Time of last address harvest") > + > +(defvar notmuch-address-completions (make-hash-table :test 'equal) > + "Hash of email addresses for completion during email composition. > + This variable is set by calling `notmuch-address-harvest'.") > + > +(defvar notmuch-address-full-harvest-finished nil > + "t indicates that full completion address harvesting has been > +finished") > + > (defcustom notmuch-address-command 'internal Default value should be changed to something matching the :type. Perhaps '(sent nil). >"The command which generates possible addresses. It must take a > single argument and output a list of possible matches, one per > line. The default value of `internal' uses built-in address > completion." Doc text can be updated as well. >:type '(radio > - (const :tag "Use internal address completion" internal) > + (list :tag "Use internal address completion" > + (radio > + :tag "Build list based on messages you have" > + :value sent > + (const :tag "sent" sent) > + (const :tag "received" received)) I would mention that received is faster here. > + (radio :tag "Filter messages used for completion" > +(const :tag "Use all messages" nil) > +(string :tag "Filter query"))) > (const :tag "Disable address completion" nil) > - (string :tag "Use external completion command" "notmuch-addresses")) > + (string :tag "Use external completion command")) > + ;; We override set so that we can clear the cache when this changes > + :set (lambda (symbol value) > + (set-default symbol value) > + (setq notmuch-address-last-harvest 0) > + (setq notmuch-address-completions (clrhash > notmuch-address-completions)) > + (setq notmuch-address-full-harvest-finished nil)) >:group 'notmuch-send >:group 'notmuch-external) > > @@ -49,17 +74,6 @@ to know how address selection is made by default." >:group 'notmuch-send >:group 'notmuch-external) > > -(defvar notmuch-address-last-harvest 0 > - "Time of last address harvest") > - > -(defvar notmuch-address-completions (make-hash-table :test 'equal) > - "Hash of email addresses for completion during email composition. > - This variable is set by calling `notmuch-address-harvest'.") > - > -(defvar notmuch-address-full-harvest-finished nil > - "t indicates that full completion address harvesting has been > -finished") > - > (defun notmuch-address-selection-function (prompt collection initial-input) >"Call (`completing-read' >PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)" > @@ -81,7 +95,8 @@ finished") > > (defun notmuch-address-setup () >(let* ((use-company (and notmuch-address-use-company > -(eq notmuch-address-command 'internal) > +notmuch-address-command > +(listp notmuch-address-command) > (require 'company nil t))) >(pair (cons notmuch-address-completion-headers-regexp >(if use-company > @@ -109,7 +124,7 @@ The candidates are taken from > `notmuch-address-completions'." > elisp-based implementation or older
[PATCH v2] emacs: Improve notmuch-message-mode initialization
Recent addition of notmuch-message-mode introduced several problems: 1. When message-setup-hook is used to set buffer local variables, these settings are not effective, because all buffer local variables are immediately erased by notmuch-message-mode initialization. 2. message-mode-hook gets invoked twice - first when message-mail invokes message-mode and second when notmuch-mua-mail invokes notmuch-message-mode. This commit fixes these problems by replacing a call to message-mail with notmuch-specific code that is (hopefully) equivalent to message-mail functionality before introduction of notmuch-message-mode. We first initialize notmuch-message-mode with notmuch-mua-pop-to-buffer, which is a modified version of message-pop-to-buffer and then call message-setup-1, which is the only functionality of message-mail that is needed for notmuch. --- emacs/notmuch-mua.el | 49 +++-- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 466edd7..a66a306 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -278,10 +278,36 @@ Note that these functions use `mail-citation-hook' if that is non-nil." (define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send) -(defun notmuch-mua-mail ( to subject other-headers other-args) - "Invoke the notmuch mail composition window. - -OTHER-ARGS are passed through to `message-mail'." +(defun notmuch-mua-pop-to-buffer (name) + "Pop to buffer NAME, and warn if it already exists and is +modified. This function is notmuch addaptation of +`message-pop-to-buffer'." + (let ((buffer (get-buffer name))) +(if (and buffer +(buffer-name buffer)) + (let ((window (get-buffer-window buffer 0))) + (if window + ;; Raise the frame already displaying the message buffer. + (progn + (gnus-select-frame-set-input-focus (window-frame window)) + (select-window window)) + (funcall (notmuch-mua-get-switch-function) buffer) + (set-buffer buffer)) + (when (and (buffer-modified-p) +(not (prog1 + (y-or-n-p + "Message already being composed; erase? ") + (message nil + (error "Message being composed"))) + (funcall (notmuch-mua-get-switch-function) name) + (set-buffer name)) +(erase-buffer) +(notmuch-message-mode))) + +(defun notmuch-mua-mail ( to subject other-headers continue + switch-function yank-action send-actions + return-action ignored) + "Invoke the notmuch mail composition window." (interactive) (when notmuch-mua-user-agent-function @@ -293,8 +319,19 @@ OTHER-ARGS are passed through to `message-mail'." (push (cons 'From (concat (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) - (apply #'message-mail to subject other-headers other-args) - (notmuch-message-mode) + (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)) + (message-setup-1 + ;; The following sexp is copied from `message-mail' + (nconc +`((To . ,(or to "")) (Subject . ,(or subject ""))) +;; C-h f compose-mail says that headers should be specified as +;; (string . value); however all the rest of message expects +;; headers to be symbols, not strings (eg message-header-format-alist). +;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html +;; We need to convert any string input, eg from rmail-start-mail. +(dolist (h other-headers other-headers) + (if (stringp (car h)) (setcar h (intern (capitalize (car h))) + yank-action send-actions return-action) (notmuch-fcc-header-setup) (message-sort-headers) (message-hide-headers) -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2] emacs: Improve notmuch-message-mode initialization
Hi, this is v2 of id:1446573161-28068-1-git-send-email-sojk...@fel.cvut.cz. As suggested by David, instead of using advices, it reimplements the needed pieces of message-mail in notmuch-specific way. Michal Sojka (1): emacs: Improve notmuch-message-mode initialization emacs/notmuch-mua.el | 49 +++-- 1 file changed, 43 insertions(+), 6 deletions(-) -- 2.6.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: Fix regression in (notmuch-)message-mode initialization
On Mon, Nov 09 2015, David Bremner wrote: > Michal Sojka <sojk...@fel.cvut.cz> writes: > >> This commit uses advice mechanism to call notmuch-message-mode instead >> of message-mode. This way, a call to message-mail initializes directly >> notmuch-message-mode rather than message-mode which is later changed >> to notmuch-message-mode. The advice is constructed in such a way, that >> it is effective only once and when called by notmuch. The second call >> to message-mode (from notmuch-message-mode) calls the original >> message-mode. > > I wanted to answer this with an alternative patch, but I haven't had > time. > > I admit to being somewhat prejudiced against shipping code with advice > in it (IMHO it's fine for user specific customization, but not very > maintainable). > > I think we might be better off in the long run replacing the call to > message-mail. Code duplication is obviously not great, but perhaps some > of the complexity of message-mail / message-pop-to-buffer can be > eliminated, since we don't need to support all of the use cases of > message-mail. OK. I'll try implement notmuch-message-mail as a simpler alternative to message-mail. Let's see how it will look like. > >> This implementation uses the new advice mechanism introduced in Emacs >> 24.4. If we want to support older version, this must be changed. > > We do try to support Emacs 23 still (except for some optional > features). Unfortunately old advice is even nastier. Hopefully, advises won't be needed then. Thanks, -Michal ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] emacs: Fix regression in (notmuch-)message-mode initialization
Recent addition of notmuch-message-mode introduced a few regressions. I use message-setup-hook to set some buffer local variables and this stopped working. The reason is that notmuch-message-mode, which cleans all buffer local variables, is called after calling message-mail, which calls the message-setup-hook. Another problem with the previous implementation might be that the message-mode-hook is called twice - first when message-mail calls message-mode and second when notmuch-message-mode calls it. This can be problematic when the hook has side effects. This commit uses advice mechanism to call notmuch-message-mode instead of message-mode. This way, a call to message-mail initializes directly notmuch-message-mode rather than message-mode which is later changed to notmuch-message-mode. The advice is constructed in such a way, that it is effective only once and when called by notmuch. The second call to message-mode (from notmuch-message-mode) calls the original message-mode. This implementation uses the new advice mechanism introduced in Emacs 24.4. If we want to support older version, this must be changed. --- emacs/notmuch-mua.el | 20 ++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index fd98ea4..540a676 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -276,6 +276,19 @@ Note that these functions use `mail-citation-hook' if that is non-nil." (define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send) +(defun message-mode--notmuch-advice () + (if (boundp 'use-notmuch-message-mode) + (progn + (makunbound 'use-notmuch-message-mode) + (notmuch-message-mode) + nil) ; Do not call original message-mode +t)) ; Call original message-mode + +;; Advice message-mode to call notmuch-message-mode when +;; use-notmuch-message-mode is bound. Otherwise message-mode is called +;; without changes. +(advice-add 'message-mode :before-while #'message-mode--notmuch-advice) + (defun notmuch-mua-mail ( to subject other-headers other-args) "Invoke the notmuch mail composition window. @@ -291,8 +304,11 @@ OTHER-ARGS are passed through to `message-mail'." (push (cons 'From (concat (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) - (apply #'message-mail to subject other-headers other-args) - (notmuch-message-mode) + ;; message-mail calls message-mode directly (via + ;; message-pop-to-buffer). We want it to call notmuch-message-mode + ;; instead. + (let ((use-notmuch-message-mode)) +(apply #'message-mail to subject other-headers other-args)) (notmuch-fcc-header-setup) (message-sort-headers) (message-hide-headers) -- 2.5.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] emacs: Make notmuch-message-mode play nicely with flyspell
Flyspell mode uses a special setting for message-mode to not spell-check message headers except Subject. Apply this setting also to notmuch-message-mode. --- emacs/notmuch-mua.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index fd98ea4..466edd7 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -273,6 +273,8 @@ Note that these functions use `mail-citation-hook' if that is non-nil." (when notmuch-address-command (notmuch-address-setup))) +(put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify) + (define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send) -- 2.5.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v8 3/3] Emacs: Add address completion based on company-mode
When company-mode is available (Emacs >= 24), address completion candidates are shown in a nice popup box. This is triggered either by pressing TAB or by waiting a while during typing an address. The completion is based entirely on the asynchronous address harvesting from notmuch-address.el so the GUI is theoretically not blocked for long time. The completion works similarly as the TAB-initiated completion from notmuch-address.el, i.e. quick harvest based on user input is executed first and only after full harvesting is finished, in-memory cached data is used. [Improved by David Bremner] --- emacs/Makefile.local | 1 + emacs/notmuch-address.el | 18 -- emacs/notmuch-company.el | 86 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 emacs/notmuch-company.el diff --git a/emacs/Makefile.local b/emacs/Makefile.local index 1109cfa..4c06c52 100644 --- a/emacs/Makefile.local +++ b/emacs/Makefile.local @@ -20,6 +20,7 @@ emacs_sources := \ $(dir)/notmuch-print.el \ $(dir)/notmuch-version.el \ $(dir)/notmuch-jump.el \ + $(dir)/notmuch-company.el $(dir)/notmuch-version.el: $(dir)/Makefile.local version.stamp $(dir)/notmuch-version.el: $(srcdir)/$(dir)/notmuch-version.el.tmpl diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index 498ef8a..49e2402 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -22,7 +22,9 @@ (require 'message) (require 'notmuch-parser) (require 'notmuch-lib) +(require 'notmuch-company) ;; +(declare-function company-manual-begin "company") (defcustom notmuch-address-command 'internal "The command which generates possible addresses. It must take a @@ -72,9 +74,21 @@ finished") (defun notmuch-address-message-insinuate () (message "calling notmuch-address-message-insinuate is no longer needed")) +(defcustom notmuch-address-use-company t + "If available, use company mode for address completion" + :type 'boolean + :group 'notmuch-send) + (defun notmuch-address-setup () - (let ((pair (cons notmuch-address-completion-headers-regexp - #'notmuch-address-expand-name))) + (let* ((use-company (and notmuch-address-use-company + (eq notmuch-address-command 'internal) + (require 'company nil t))) +(pair (cons notmuch-address-completion-headers-regexp +(if use-company +#'company-manual-begin + #'notmuch-address-expand-name + (when use-company + (notmuch-company-setup)) (unless (memq pair message-completion-alist) (setq message-completion-alist (push pair message-completion-alist) diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el new file mode 100644 index 000..add3161 --- /dev/null +++ b/emacs/notmuch-company.el @@ -0,0 +1,86 @@ +;; notmuch-company.el --- Mail address completion for notmuch via company-mode -*- lexical-binding: t -*- + +;; Authors: Trevor Jim <t...@mac.com> +;; Michal Sojka <sojk...@fel.cvut.cz> +;; +;; Keywords: mail, completion + +;; 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/>. + +;;; Commentary: + +;; To enable this, install company mode (https://company-mode.github.io/) +;; +;; NB company-minimum-prefix-length defaults to 3 so you don't get +;; completion unless you type 3 characters + +;;; Code: + +(eval-when-compile (require 'cl)) + +(defvar notmuch-company-last-prefix nil) +(make-variable-buffer-local 'notmuch-company-last-prefix) +(declare-function company-begin-backend "company") +(declare-function company-grab "company") +(declare-function company-mode "company") +(declare-function company-manual-begin "company") +(defvar company-backends) + +(declare-function notmuch-address-harvest "notmuch-address") +(declare-function notmuch-address-harvest-trigger "notmuch-address") +(declare-function notmuch-address-matching "notmuch-address") +(defvar notmuch-address-full-harvest-finished) +(defvar notmuch-address-completion-headers-regexp) + +;;;###autoload +(defun notmuch-company-setup () + (company-mode) + (make-local-variable 'company-backends) + (setq company-backends '(notmu
[PATCH v8 1/3] emacs: replace use of notmuch-address-message-insinuate
From: David BremnerThis allows e.g. Gnus users to load this file without changing message-mode behaviour. This will disable completion for those that did not customize the variable but relied on the existence of a file named "notmuch-addresses" in their path. In the next commit the default behaviour will change to use a "workalike" internal completion mechanism. --- emacs/notmuch-address.el | 19 --- emacs/notmuch-mua.el | 4 +++- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index fde3c1b..e2af879 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -23,11 +23,13 @@ ;; -(defcustom notmuch-address-command "notmuch-addresses" +(defcustom notmuch-address-command nil "The command which generates possible addresses. It must take a single argument and output a list of possible matches, one per -line." - :type 'string +line. The default value of nil disables address completion." + :type '(radio + (const :tag "Disable address completion" nil) + (string :tag "Use external completion command" "notmuch-addresses")) :group 'notmuch-send :group 'notmuch-external) @@ -55,10 +57,12 @@ to know how address selection is made by default." (defvar notmuch-address-history nil) (defun notmuch-address-message-insinuate () + (message "calling notmuch-address-message-insinuate is no longer needed")) + +(defun notmuch-address-setup () (unless (memq notmuch-address-message-alist-member message-completion-alist) (setq message-completion-alist (push notmuch-address-message-alist-member message-completion-alist - (defun notmuch-address-options (original) (process-lines notmuch-address-command original)) @@ -109,11 +113,4 @@ to know how address selection is made by default." (not (file-directory-p bin (throw 'found-command bin -;; If we can find the program specified by `notmuch-address-command', -;; insinuate ourselves into `message-mode'. -(when (notmuch-address-locate-command notmuch-address-command) - (notmuch-address-message-insinuate)) - -;; - (provide 'notmuch-address) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 57465b2..fd98ea4 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -269,7 +269,9 @@ Note that these functions use `mail-citation-hook' if that is non-nil." (set-buffer-modified-p nil)) (define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]" - "Notmuch message composition mode. Mostly like `message-mode'") + "Notmuch message composition mode. Mostly like `message-mode'" + (when notmuch-address-command +(notmuch-address-setup))) (define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send) -- 2.5.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v8 2/3] Emacs: Add address completion mechanism implemented in elisp
Currently, notmuch has an address completion mechanism that requires external command to provide completion candidates. This commit adds a completion mechanism inspired by https://github.com/tjim/nevermore, which is implemented in Emacs lisp only. The preexisting address completion mechanism, activated by pressing TAB on To/Cc lines, is extended to use the new mechanism when notmuch-address-command to 'internal, which is the new default. The core of the new mechanism is the function notmuch-address-harvest, which collects the completion candidates from the notmuch database and stores them in notmuch-address-completions variable. The address harvesting can run either synchronously (same as with the previous mechanism) or asynchronously. When the user presses TAB for the first time, synchronous harvesting limited to user entered text is performed. If the entered text is reasonably long, this operation is relatively fast. Then, asynchronous harvesting over the full database is triggered. This operation may take long time (minutes on rotating disk). After it finishes, no harvesting is normally performed again and subsequent completion requests use the harvested data cached in memory. Completion cache is updated after 24 hours. Note that this commit restores (different) completion functionality for users when the user used external command named "notmuch-addresses", i.e. the old default. The result will be that the user will use the new mechanism instead of this command. I believe that many users may not even recognize this because the new mechanism works the same as http://commonmeasure.org/~jkr/git/notmuch_addresses.git and perhaps also as other commands suggested at http://notmuchmail.org/emacstips/#address_completion. [This feature was significantly improved by David Bremner and Mark Walters] --- emacs/notmuch-address.el | 192 ++- emacs/notmuch-lib.el | 3 + 2 files changed, 159 insertions(+), 36 deletions(-) diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index e2af879..498ef8a 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -20,14 +20,17 @@ ;; Authors: David Edmondson(require 'message) - +(require 'notmuch-parser) +(require 'notmuch-lib) ;; -(defcustom notmuch-address-command nil +(defcustom notmuch-address-command 'internal "The command which generates possible addresses. It must take a single argument and output a list of possible matches, one per -line. The default value of nil disables address completion." +line. The default value of `internal' uses built-in address +completion." :type '(radio + (const :tag "Use internal address completion" internal) (const :tag "Disable address completion" nil) (string :tag "Use external completion command" "notmuch-addresses")) :group 'notmuch-send @@ -44,15 +47,25 @@ to know how address selection is made by default." :group 'notmuch-send :group 'notmuch-external) +(defvar notmuch-address-last-harvest 0 + "Time of last address harvest") + +(defvar notmuch-address-completions (make-hash-table :test 'equal) + "Hash of email addresses for completion during email composition. + This variable is set by calling `notmuch-address-harvest'.") + +(defvar notmuch-address-full-harvest-finished nil + "t indicates that full completion address harvesting has been +finished") + (defun notmuch-address-selection-function (prompt collection initial-input) "Call (`completing-read' PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)" (completing-read prompt collection nil nil initial-input 'notmuch-address-history)) -(defvar notmuch-address-message-alist-member - '("^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):" - . notmuch-address-expand-name)) +(defvar notmuch-address-completion-headers-regexp + "^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):") (defvar notmuch-address-history nil) @@ -60,39 +73,67 @@ to know how address selection is made by default." (message "calling notmuch-address-message-insinuate is no longer needed")) (defun notmuch-address-setup () - (unless (memq notmuch-address-message-alist-member message-completion-alist) -(setq message-completion-alist - (push notmuch-address-message-alist-member message-completion-alist + (let ((pair (cons notmuch-address-completion-headers-regexp + #'notmuch-address-expand-name))) + (unless (memq pair message-completion-alist) + (setq message-completion-alist + (push pair message-completion-alist) + +(defun notmuch-address-matching (substring) + "Returns a list of completion candidates matching SUBSTRING. +The candidates are taken from `notmuch-address-completions'." + (let ((candidates) + (re (regexp-quote substring))) +(maphash (lambda (key val) +
[PATCH v8 0/3] elisp + company completion patches
Hi David, Mark and others, thank you for pushing these patches forward. I gave them some testing and they work as expected. In this version I addressed the comments to v7 including Tomi's request for rewording :) The commit message of 2/3 was also updated to reflect changes in the code. Cheers. -Michal David Bremner (1): emacs: replace use of notmuch-address-message-insinuate Michal Sojka (2): Emacs: Add address completion mechanism implemented in elisp Emacs: Add address completion based on company-mode emacs/Makefile.local | 1 + emacs/notmuch-address.el | 213 ++- emacs/notmuch-company.el | 86 +++ emacs/notmuch-lib.el | 3 + emacs/notmuch-mua.el | 4 +- 5 files changed, 265 insertions(+), 42 deletions(-) create mode 100644 emacs/notmuch-company.el -- 2.5.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
On Fri, Jan 09 2015, Tomi Ollila wrote: > On Fri, Jan 09 2015, Michal Sojka wrote: > >> Hi, >> >> sorry for longer response time :) >> >> On Thu, Jan 01 2015, Tomi Ollila wrote: >>> On Wed, Dec 31 2014, David Bremner wrote: >>> >>>> Michal Sojka writes: >>>> >>>>> This option allows to configure the criterion for duplicate address >>>>> filtering. Without this option, all unique combinations of name and >>>>> address parts are printed. This option allows to filter the output >>>>> more, for example to only contain unique address parts. >>>> >>>> I had the feeling there was some "controversy" about the UI here, but >>>> following back the 3 versions of the series I didn't see it. Does that >>>> mean we just need to sanity check the code, or are there outstanding >>>> bikes to shed? >> >> I'd tend to rename this option to --unique as it was in some previous >> version of the patch. Another thing in my mind is the implementation of >> the --complete option mentioned in id:878uid9qjl.fsf at nautilus.nautilus. >> This would also involve some kind of address filtering. I'll look into >> this and send patches later. >> >>> I have intentionally been guiet on this during the review process of the >>> other patches to not slow down the acceptance of the others. I have not >>> got enough time to look the implemenentation or think this last patch >>> further -- from the user interface point of view I recall seeing there >>> both useless features (but which might be warranted by implementation >>> simplicity) and missing features (but which might not be there due to >>> difficulty in implementation). Also, I am not sure whether the --filter-by >>> is good option (and options descriptive...)... >> >> I'd be interested in what are these "missing features". > > Last night when I tried to catch sleep I was also thinking of this... > ... let's see what I remember... > > First, Currently if we have addresses: > > "Uni Que" > "Uni Que" > > I presume these are thought as a separate addresses -- and an option to > thought these as the same would be useful. Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold from my patch. > but let's consider second set of addresses: > > "Uni Que" > "Uni Keko" > > Now, if there were an option to consider these 2 as the same, that would > hide user from one of the names -- It is clear that "Uni Que" is the right > one but if only "Uni Keko" (sleepyhead, that is) is shown user don't have > a choice to select the right one. I am not sure what the use case for > "uniquing" these 2 were. For example, when you are interested in the number of people involved in a discussion. You care only about the address and not about the names. Perhaps you'd like to see only the addresses in the output and not the names in this case, wouldn't you? > Finally (for now), 3rd set of addresses > > "Uni Que" > "Uni Que" > > Now, if there were an option to consider these 2 as same, and > user is then given "Uni Que" (which clearly is > the wrong one) I don't see the usefullness of this option... I agree. This would correspond to --unique=name. So I'll drop this option. -Michal
[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
Hi, sorry for longer response time :) On Thu, Jan 01 2015, Tomi Ollila wrote: > On Wed, Dec 31 2014, David Bremner wrote: > >> Michal Sojka writes: >> >>> This option allows to configure the criterion for duplicate address >>> filtering. Without this option, all unique combinations of name and >>> address parts are printed. This option allows to filter the output >>> more, for example to only contain unique address parts. >> >> I had the feeling there was some "controversy" about the UI here, but >> following back the 3 versions of the series I didn't see it. Does that >> mean we just need to sanity check the code, or are there outstanding >> bikes to shed? I'd tend to rename this option to --unique as it was in some previous version of the patch. Another thing in my mind is the implementation of the --complete option mentioned in id:878uid9qjl.fsf at nautilus.nautilus. This would also involve some kind of address filtering. I'll look into this and send patches later. > I have intentionally been guiet on this during the review process of the > other patches to not slow down the acceptance of the others. I have not > got enough time to look the implemenentation or think this last patch > further -- from the user interface point of view I recall seeing there > both useless features (but which might be warranted by implementation > simplicity) and missing features (but which might not be there due to > difficulty in implementation). Also, I am not sure whether the --filter-by > is good option (and options descriptive...)... I'd be interested in what are these "missing features". Cheers, -Michal
Re: [PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
On Fri, Jan 09 2015, Michal Sojka wrote: On Fri, Jan 09 2015, Tomi Ollila wrote: On Fri, Jan 09 2015, Michal Sojka sojk...@fel.cvut.cz wrote: Hi, sorry for longer response time :) On Thu, Jan 01 2015, Tomi Ollila wrote: On Wed, Dec 31 2014, David Bremner da...@tethera.net wrote: Michal Sojka sojk...@fel.cvut.cz writes: This option allows to configure the criterion for duplicate address filtering. Without this option, all unique combinations of name and address parts are printed. This option allows to filter the output more, for example to only contain unique address parts. I had the feeling there was some controversy about the UI here, but following back the 3 versions of the series I didn't see it. Does that mean we just need to sanity check the code, or are there outstanding bikes to shed? I'd tend to rename this option to --unique as it was in some previous version of the patch. Another thing in my mind is the implementation of the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus. This would also involve some kind of address filtering. I'll look into this and send patches later. I have intentionally been guiet on this during the review process of the other patches to not slow down the acceptance of the others. I have not got enough time to look the implemenentation or think this last patch further -- from the user interface point of view I recall seeing there both useless features (but which might be warranted by implementation simplicity) and missing features (but which might not be there due to difficulty in implementation). Also, I am not sure whether the --filter-by is good option (and options descriptive...)... I'd be interested in what are these missing features. Last night when I tried to catch sleep I was also thinking of this... ... let's see what I remember... First, Currently if we have addresses: Uni Que uni...@example.org Uni Que uni...@example.org I presume these are thought as a separate addresses -- and an option to thought these as the same would be useful. Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold from my patch. but let's consider second set of addresses: Uni Que uni...@example.org Uni Keko uni...@example.org Now, if there were an option to consider these 2 as the same, that would hide user from one of the names -- It is clear that Uni Que is the right one but if only Uni Keko (sleepyhead, that is) is shown user don't have a choice to select the right one. I am not sure what the use case for uniquing these 2 were. For example, when you are interested in the number of people involved in a discussion. You care only about the address and not about the names. Perhaps you'd like to see only the addresses in the output and not the names in this case, wouldn't you? I meant something like the patch bellow. Unique options would be no/yes/yes-but-case-insensitive and the type of uniqueness would be determined by the --output flags. I don't like one thing on this approach: --output flags now determine visibility of both rows and columns in the output. But this is already present in master, because we have count column there. -Michal diff --git a/notmuch-search.c b/notmuch-search.c index 14b9f01..760f59a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -34,6 +34,8 @@ typedef enum { OUTPUT_SENDER = 1 5, OUTPUT_RECIPIENTS = 1 6, OUTPUT_COUNT = 1 7, +OUTPUT_NAME= 1 8, +OUTPUT_ADDR= 1 9, } output_t; typedef enum { @@ -43,6 +45,12 @@ typedef enum { NOTMUCH_FORMAT_SEXP } format_sel_t; +typedef enum { +UNIQUE_NO = 0, +UNIQUE_YES, +UNIQUE_CASEFOLD, +} unique_t; + typedef struct { notmuch_database_t *notmuch; format_sel_t format_sel; @@ -55,6 +63,7 @@ typedef struct { int limit; int dupe; GHashTable *addresses; +unique_t unique; } search_context_t; typedef struct { @@ -243,18 +252,45 @@ do_search_threads (search_context_t *ctx) return 0; } -/* Returns TRUE iff name and addr is duplicate. If not, stores the - * name/addr pair in order to detect subsequent duplicates. */ +/* Returns TRUE iff name and/or addr is considered duplicate. If not, + * stores the name/addr pair in order to detect subsequent + * duplicates. */ static notmuch_bool_t is_duplicate (const search_context_t *ctx, const char *name, const char *addr) { notmuch_bool_t duplicate; char *key; +gchar *addrfold = NULL; +gchar *namefold = NULL; mailbox_t *mailbox; -key = talloc_asprintf (ctx-format, %s %s, name, addr); +if (ctx-unique == UNIQUE_CASEFOLD) { + addrfold = g_utf8_casefold (addr, -1); + namefold = g_utf8_casefold (name, -1); +} + +switch (ctx-output (OUTPUT_NAME | OUTPUT_ADDR)) { +case OUTPUT_NAME | OUTPUT_ADDR: + key = talloc_asprintf (ctx-format, %s %s, namefold ? namefold : name
Re: [PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
On Fri, Jan 09 2015, Tomi Ollila wrote: On Fri, Jan 09 2015, Michal Sojka sojk...@fel.cvut.cz wrote: Hi, sorry for longer response time :) On Thu, Jan 01 2015, Tomi Ollila wrote: On Wed, Dec 31 2014, David Bremner da...@tethera.net wrote: Michal Sojka sojk...@fel.cvut.cz writes: This option allows to configure the criterion for duplicate address filtering. Without this option, all unique combinations of name and address parts are printed. This option allows to filter the output more, for example to only contain unique address parts. I had the feeling there was some controversy about the UI here, but following back the 3 versions of the series I didn't see it. Does that mean we just need to sanity check the code, or are there outstanding bikes to shed? I'd tend to rename this option to --unique as it was in some previous version of the patch. Another thing in my mind is the implementation of the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus. This would also involve some kind of address filtering. I'll look into this and send patches later. I have intentionally been guiet on this during the review process of the other patches to not slow down the acceptance of the others. I have not got enough time to look the implemenentation or think this last patch further -- from the user interface point of view I recall seeing there both useless features (but which might be warranted by implementation simplicity) and missing features (but which might not be there due to difficulty in implementation). Also, I am not sure whether the --filter-by is good option (and options descriptive...)... I'd be interested in what are these missing features. Last night when I tried to catch sleep I was also thinking of this... ... let's see what I remember... First, Currently if we have addresses: Uni Que uni...@example.org Uni Que uni...@example.org I presume these are thought as a separate addresses -- and an option to thought these as the same would be useful. Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold from my patch. but let's consider second set of addresses: Uni Que uni...@example.org Uni Keko uni...@example.org Now, if there were an option to consider these 2 as the same, that would hide user from one of the names -- It is clear that Uni Que is the right one but if only Uni Keko (sleepyhead, that is) is shown user don't have a choice to select the right one. I am not sure what the use case for uniquing these 2 were. For example, when you are interested in the number of people involved in a discussion. You care only about the address and not about the names. Perhaps you'd like to see only the addresses in the output and not the names in this case, wouldn't you? Finally (for now), 3rd set of addresses Uni Que uni...@example.org Uni Que unique@foobar.invalid Now, if there were an option to consider these 2 as same, and user is then given Uni Que unique@foobar.invalid (which clearly is the wrong one) I don't see the usefullness of this option... I agree. This would correspond to --unique=name. So I'll drop this option. -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
Hi, sorry for longer response time :) On Thu, Jan 01 2015, Tomi Ollila wrote: On Wed, Dec 31 2014, David Bremner da...@tethera.net wrote: Michal Sojka sojk...@fel.cvut.cz writes: This option allows to configure the criterion for duplicate address filtering. Without this option, all unique combinations of name and address parts are printed. This option allows to filter the output more, for example to only contain unique address parts. I had the feeling there was some controversy about the UI here, but following back the 3 versions of the series I didn't see it. Does that mean we just need to sanity check the code, or are there outstanding bikes to shed? I'd tend to rename this option to --unique as it was in some previous version of the patch. Another thing in my mind is the implementation of the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus. This would also involve some kind of address filtering. I'll look into this and send patches later. I have intentionally been guiet on this during the review process of the other patches to not slow down the acceptance of the others. I have not got enough time to look the implemenentation or think this last patch further -- from the user interface point of view I recall seeing there both useless features (but which might be warranted by implementation simplicity) and missing features (but which might not be there due to difficulty in implementation). Also, I am not sure whether the --filter-by is good option (and options descriptive...)... I'd be interested in what are these missing features. Cheers, -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
New "notmuch address" command
On P?, pro 12 2014, Lele Gaifax wrote: > Michal Sojka writes: > >>> An (almost) equivalent of "notmuch-addrlookup foo" could be "notmuch >>> address to:foo* OR from:foo*", but it has at least one indesiderable >>> difference: it seems considering the "CC" field, but always emits the >>> "TO" content (i.e., assuming I have a message I sent to "john at doe.com" >>> and CCed to "foo at bar.com", "notmuch address to:foo" emits >>> "john at doe.com", not "foo at bar.com") so the candidates it generates are >>> way too much. >>> >>> I don't know it that's done on purpose (I clearly miss the use case if >>> so). >> >> Yes, this is expected behavior. Notmuch address is basically a wrapper >> around search command. The command does not interpret the query at all, >> because there might be no from:/to: term. The use case was to SIMPLIFY >> address completers. > > Ok, even if I still miss the point of searching for an address and > obtaining (only, see below) something (apparently) unrelated. > >>> I wonder if it would be reasonable adding a "--complete" flag to the >>> "address" command that selects a more specific behaviour, so that >>> "notmuch address --complete foo": >> ... >>> b) searches the given text only in the related headers (hiding the >>>difference between "incoming" and "outgoing" messages, >> >> This should be configurable, because --output=sender is much faster than >> --output=recipients. I think that ideal address completion should offer >> you the addresses you have already written to, i.e. >> >> notmuch address --output=recipient from:my at address to:"prefix*" >> >> But this may be too slow on non-SSD disks. Some users may therefore prefer >> >> notmuch address --output=sender to:my at address from:"prefix*" >> >> which would be faster, but also includes every spammer/robot/... who >> sends anything to you. > > Yes, seems reasonable! > >>> and not >>>considering the body at all) >> >> What considers body now? > > Well, "notmuch address foo" currently does that, and that sounds useful, > to obtain a list of recipients who talked about "foo". > >>> c) avoids the "bug"/"feature" explained above >> >> Yes, if you know the substring you are looking for, implementing a >> filter would be trivial. > > It's not just a matter of filtering, but rather *which* address is > emitted: trying it out, in the case above the "foo at bar.com" is not even > mentioned in the output, because it appears only as a CCed recipient. Are you saying that Cc address is not printed or that you want it not to be printed? If the former than it is a bug. The following test passes for me, i.e. foo at bar.com is printed. diff --git a/test/T095-address.sh b/test/T095-address.sh index ed0cac7..17a7b08 100755 --- a/test/T095-address.sh +++ b/test/T095-address.sh @@ -145,4 +145,13 @@ cat <EXPECTED EOF test_expect_equal_file OUTPUT EXPECTED +test_begin_subtest "Cc address is printed while searching for To address" +add_message [to]=john at doe.com [cc]=foo at bar.com +notmuch address --output=recipients to:john at doe.com > OUTPUT +cat <EXPECTED +john at doe.com +foo at bar.com +EOF +test_expect_equal_file OUTPUT EXPECTED + test_done Cheers, -Michal
Re: New notmuch address command
On Pá, pro 12 2014, Lele Gaifax wrote: Michal Sojka sojk...@fel.cvut.cz writes: An (almost) equivalent of notmuch-addrlookup foo could be notmuch address to:foo* OR from:foo*, but it has at least one indesiderable difference: it seems considering the CC field, but always emits the TO content (i.e., assuming I have a message I sent to j...@doe.com and CCed to f...@bar.com, notmuch address to:foo emits j...@doe.com, not f...@bar.com) so the candidates it generates are way too much. I don't know it that's done on purpose (I clearly miss the use case if so). Yes, this is expected behavior. Notmuch address is basically a wrapper around search command. The command does not interpret the query at all, because there might be no from:/to: term. The use case was to SIMPLIFY address completers. Ok, even if I still miss the point of searching for an address and obtaining (only, see below) something (apparently) unrelated. I wonder if it would be reasonable adding a --complete flag to the address command that selects a more specific behaviour, so that notmuch address --complete foo: ... b) searches the given text only in the related headers (hiding the difference between incoming and outgoing messages, This should be configurable, because --output=sender is much faster than --output=recipients. I think that ideal address completion should offer you the addresses you have already written to, i.e. notmuch address --output=recipient from:my@address to:prefix* But this may be too slow on non-SSD disks. Some users may therefore prefer notmuch address --output=sender to:my@address from:prefix* which would be faster, but also includes every spammer/robot/... who sends anything to you. Yes, seems reasonable! and not considering the body at all) What considers body now? Well, notmuch address foo currently does that, and that sounds useful, to obtain a list of recipients who talked about foo. c) avoids the bug/feature explained above Yes, if you know the substring you are looking for, implementing a filter would be trivial. It's not just a matter of filtering, but rather *which* address is emitted: trying it out, in the case above the f...@bar.com is not even mentioned in the output, because it appears only as a CCed recipient. Are you saying that Cc address is not printed or that you want it not to be printed? If the former than it is a bug. The following test passes for me, i.e. f...@bar.com is printed. diff --git a/test/T095-address.sh b/test/T095-address.sh index ed0cac7..17a7b08 100755 --- a/test/T095-address.sh +++ b/test/T095-address.sh @@ -145,4 +145,13 @@ cat EOF EXPECTED EOF test_expect_equal_file OUTPUT EXPECTED +test_begin_subtest Cc address is printed while searching for To address +add_message [to]=j...@doe.com [cc]=f...@bar.com +notmuch address --output=recipients to:j...@doe.com OUTPUT +cat EOF EXPECTED +j...@doe.com +f...@bar.com +EOF +test_expect_equal_file OUTPUT EXPECTED + test_done Cheers, -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
New "notmuch address" command
Hi Lele, On Fri, Dec 12 2014, Lele Gaifax wrote: > Hi all again, > > I'm happily using "notmuch-addrlookup"[1] as "notmuch-address-command", in > my Emacs configuration. > > As explained in my other message, yesterday I spent some time tweaking > that configuration and tried to replace it with the new "notmuch > address" introduced in version 0.19. > > An (almost) equivalent of "notmuch-addrlookup foo" could be "notmuch > address to:foo* OR from:foo*", but it has at least one indesiderable > difference: it seems considering the "CC" field, but always emits the > "TO" content (i.e., assuming I have a message I sent to "john at doe.com" > and CCed to "foo at bar.com", "notmuch address to:foo" emits > "john at doe.com", not "foo at bar.com") so the candidates it generates are > way too much. > > I don't know it that's done on purpose (I clearly miss the use case if > so). Yes, this is expected behavior. Notmuch address is basically a wrapper around search command. The command does not interpret the query at all, because there might be no from:/to: term. The use case was to SIMPLIFY address completers. > I wonder if it would be reasonable adding a "--complete" flag to the > "address" command that selects a more specific behaviour, so that > "notmuch address --complete foo": This would definitely be useful. > a) automatically performs a partial match (i.e. it adds the '*' suffix >on its own) OK > b) searches the given text only in the related headers (hiding the >difference between "incoming" and "outgoing" messages, This should be configurable, because --output=sender is much faster than --output=recipients. I think that ideal address completion should offer you the addresses you have already written to, i.e. notmuch address --output=recipient from:my at address to:"prefix*" But this may be too slow on non-SSD disks. Some users may therefore prefer notmuch address --output=sender to:my at address from:"prefix*" which would be faster, but also includes every spammer/robot/... who sends anything to you. > and not >considering the body at all) What considers body now? > > c) avoids the "bug"/"feature" explained above Yes, if you know the substring you are looking for, implementing a filter would be trivial. > What do you think? Another question is that uses may want to select which my at address to use. So maybe you can add --my-address option that allow specifying one or more addresses. If this option would not be given, all configured addresses in .notmuch-config would be used. So I think that --complete should just construct the query containing from:/to: terms and this should be concatenated with what user specified as a query. For example: notmuch address --complete prefix tag:attachment -Michal
Re: New notmuch address command
Hi Lele, On Fri, Dec 12 2014, Lele Gaifax wrote: Hi all again, I'm happily using notmuch-addrlookup[1] as notmuch-address-command, in my Emacs configuration. As explained in my other message, yesterday I spent some time tweaking that configuration and tried to replace it with the new notmuch address introduced in version 0.19. An (almost) equivalent of notmuch-addrlookup foo could be notmuch address to:foo* OR from:foo*, but it has at least one indesiderable difference: it seems considering the CC field, but always emits the TO content (i.e., assuming I have a message I sent to j...@doe.com and CCed to f...@bar.com, notmuch address to:foo emits j...@doe.com, not f...@bar.com) so the candidates it generates are way too much. I don't know it that's done on purpose (I clearly miss the use case if so). Yes, this is expected behavior. Notmuch address is basically a wrapper around search command. The command does not interpret the query at all, because there might be no from:/to: term. The use case was to SIMPLIFY address completers. I wonder if it would be reasonable adding a --complete flag to the address command that selects a more specific behaviour, so that notmuch address --complete foo: This would definitely be useful. a) automatically performs a partial match (i.e. it adds the '*' suffix on its own) OK b) searches the given text only in the related headers (hiding the difference between incoming and outgoing messages, This should be configurable, because --output=sender is much faster than --output=recipients. I think that ideal address completion should offer you the addresses you have already written to, i.e. notmuch address --output=recipient from:my@address to:prefix* But this may be too slow on non-SSD disks. Some users may therefore prefer notmuch address --output=sender to:my@address from:prefix* which would be faster, but also includes every spammer/robot/... who sends anything to you. and not considering the body at all) What considers body now? c) avoids the bug/feature explained above Yes, if you know the substring you are looking for, implementing a filter would be trivial. What do you think? Another question is that uses may want to select which my@address to use. So maybe you can add --my-address option that allow specifying one or more addresses. If this option would not be given, all configured addresses in .notmuch-config would be used. So I think that --complete should just construct the query containing from:/to: terms and this should be concatenated with what user specified as a query. For example: notmuch address --complete prefix tag:attachment -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3] nmbug: Add an 'init' command
Hi Trevor, On ?t, ??j 28 2014, W. Trevor King wrote: > For folks that want to start versioning a new tag-space, instead of > cloning one that someone else has already started. > > The empty-blob hash-object call avoids errors like: > > $ nmbug commit > error: invalid object 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 for > 'tags/...' > fatal: git-write-tree: error building trees > 'git HASH(0x9ef3eb8) write-tree' exited with nonzero value > > David Bremner suggested [1]: > > $ git hash-object -w /dev/null > > instead of my Python version of: > > $ git hash-object -w --stdin <&- > > but I expect that closing stdin is more portable than the /dev/null > path (which doesn't exist on Windows, for example). > > [1]: id:87y4vu6uvf.fsf at maritornes.cs.unb.ca > http://thread.gmane.org/gmane.mail.notmuch.general/18626/focus=18720 > --- > The only change since v2 [1] is a commit-message tweak: > > * Mention Windows as an OS with stdin but no /dev/null [2]. > > Cheers, > Trevor > > [1]: id:eaa9cf1cb3c00c591dc675c0f314ca31909ff74c.1412965476.git.wking at > tremily.us > http://thread.gmane.org/gmane.mail.notmuch.general/19289 > [2]: id:20141011071000.GB10926 at odin.tremily.us > http://article.gmane.org/gmane.mail.notmuch.general/19294 > > devel/nmbug/nmbug | 24 > 1 file changed, 24 insertions(+) > > diff --git a/devel/nmbug/nmbug b/devel/nmbug/nmbug > index 9402ead..23bac5c 100755 > --- a/devel/nmbug/nmbug > +++ b/devel/nmbug/nmbug > @@ -373,6 +373,29 @@ def fetch(remote=None): > _git(args=args, wait=True) > > > +def init(remote=None): > +""" > +Create an empty nmbug repository. > + > +This wraps 'git init' with a few extra steps to support subsequent > +status and commit commands. > +""" > +with _tempfile.TemporaryDirectory(prefix='nmbug-init.') as workdir: > +_spawn( > +args=['git', 'init', '--separate-git-dir', NMBGIT, workdir], > +wait=True) > +_git(args=['config', '--unset', 'core.worktree'], wait=True) > +_git(args=['config', 'core.bare', 'true'], wait=True) Why do you create a non-bare repository and then make it bare? Before I discovered this patch, I created the nmbug repository by hand and it was possible to do it with bare repo from beginning. The following code seems to work and is a bit simpler: def init(remote=None): """ Create an empty nmbug repository. This wraps 'git init' with a few extra steps to support subsequent status and commit commands. """ _spawn( args=['git', '--git-dir', NMBGIT, 'init', '--bare'], wait=True) # create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) _git(args=['hash-object', '-w', '--stdin'], input='', wait=True) _git( args=[ 'commit', '--allow-empty', '-m', 'Start a new nmbug repository' ], additional_env={'GIT_WORK_TREE': NMBGIT}, wait=True) Note that in the initial commit I set the work tree to NMBGIT. This is because 'git commit' needs some work tree, but in our case it doesn't matter which one because the commit is empty. -Michal > +# create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) > +_git(args=['hash-object', '-w', '--stdin'], input='', wait=True) > +_git( > +args=[ > +'commit', '--allow-empty', '-m', 'Start a new nmbug > repository' > +], > +additional_env={'GIT_WORK_TREE': workdir}, > +wait=True) > + > + > def checkout(): > """ > Update the notmuch database from Git. > @@ -703,6 +726,7 @@ if __name__ == '__main__': > 'clone', > 'commit', > 'fetch', > +'init', > 'log', > 'merge', > 'pull', > -- > 2.1.0.60.g85f0837 > > ___ > notmuch mailing list > notmuch at notmuchmail.org > http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v3] nmbug: Add an 'init' command
Hi Trevor, On Út, říj 28 2014, W. Trevor King wrote: For folks that want to start versioning a new tag-space, instead of cloning one that someone else has already started. The empty-blob hash-object call avoids errors like: $ nmbug commit error: invalid object 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 for 'tags/...' fatal: git-write-tree: error building trees 'git HASH(0x9ef3eb8) write-tree' exited with nonzero value David Bremner suggested [1]: $ git hash-object -w /dev/null instead of my Python version of: $ git hash-object -w --stdin - but I expect that closing stdin is more portable than the /dev/null path (which doesn't exist on Windows, for example). [1]: id:87y4vu6uvf@maritornes.cs.unb.ca http://thread.gmane.org/gmane.mail.notmuch.general/18626/focus=18720 --- The only change since v2 [1] is a commit-message tweak: * Mention Windows as an OS with stdin but no /dev/null [2]. Cheers, Trevor [1]: id:eaa9cf1cb3c00c591dc675c0f314ca31909ff74c.1412965476.git.wk...@tremily.us http://thread.gmane.org/gmane.mail.notmuch.general/19289 [2]: id:20141011071000.gb10...@odin.tremily.us http://article.gmane.org/gmane.mail.notmuch.general/19294 devel/nmbug/nmbug | 24 1 file changed, 24 insertions(+) diff --git a/devel/nmbug/nmbug b/devel/nmbug/nmbug index 9402ead..23bac5c 100755 --- a/devel/nmbug/nmbug +++ b/devel/nmbug/nmbug @@ -373,6 +373,29 @@ def fetch(remote=None): _git(args=args, wait=True) +def init(remote=None): + +Create an empty nmbug repository. + +This wraps 'git init' with a few extra steps to support subsequent +status and commit commands. + +with _tempfile.TemporaryDirectory(prefix='nmbug-init.') as workdir: +_spawn( +args=['git', 'init', '--separate-git-dir', NMBGIT, workdir], +wait=True) +_git(args=['config', '--unset', 'core.worktree'], wait=True) +_git(args=['config', 'core.bare', 'true'], wait=True) Why do you create a non-bare repository and then make it bare? Before I discovered this patch, I created the nmbug repository by hand and it was possible to do it with bare repo from beginning. The following code seems to work and is a bit simpler: def init(remote=None): Create an empty nmbug repository. This wraps 'git init' with a few extra steps to support subsequent status and commit commands. _spawn( args=['git', '--git-dir', NMBGIT, 'init', '--bare'], wait=True) # create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) _git(args=['hash-object', '-w', '--stdin'], input='', wait=True) _git( args=[ 'commit', '--allow-empty', '-m', 'Start a new nmbug repository' ], additional_env={'GIT_WORK_TREE': NMBGIT}, wait=True) Note that in the initial commit I set the work tree to NMBGIT. This is because 'git commit' needs some work tree, but in our case it doesn't matter which one because the commit is empty. -Michal +# create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) +_git(args=['hash-object', '-w', '--stdin'], input='', wait=True) +_git( +args=[ +'commit', '--allow-empty', '-m', 'Start a new nmbug repository' +], +additional_env={'GIT_WORK_TREE': workdir}, +wait=True) + + def checkout(): Update the notmuch database from Git. @@ -703,6 +726,7 @@ if __name__ == '__main__': 'clone', 'commit', 'fetch', +'init', 'log', 'merge', 'pull', -- 2.1.0.60.g85f0837 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/2] doc: Minor fixes related to notmuch-address
- Add notmuch-address to HTML index - Remove extra '*' --- doc/index.rst| 1 + doc/man1/notmuch.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index ba6d5b4..3f0e6e6 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -8,6 +8,7 @@ Contents: :titlesonly: man1/notmuch + man1/notmuch-address man1/notmuch-compact man1/notmuch-config man1/notmuch-count diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst index 98590a4..b33738e 100644 --- a/doc/man1/notmuch.rst +++ b/doc/man1/notmuch.rst @@ -129,7 +129,7 @@ SEE ALSO **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**, **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**, -***notmuch-address(1)** +**notmuch-address(1)** The notmuch website: **http://notmuchmail.org** -- 2.1.1
[PATCH 1/2] NEWS: notmuch address
--- NEWS | 8 1 file changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 8d7ed0a..16cc002 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,14 @@ Stopped `notmuch dump` failing if someone writes to the database limits output of message IDs to messages matching search terms that have at least `N` files associated with them. +Added `notmuch address` subcommand + + This new subcommand searches for messages matching the given search + terms, and prints the addresses from them. Duplicate addresses are + filtered out. The `--output` option controls which of the following + information is printed: sender addresses, recipient addresses and + count of duplicate addresses. + Emacs Interface --- -- 2.1.1
[PATCH 1/2] NEWS: notmuch address
--- NEWS | 8 1 file changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 8d7ed0a..16cc002 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,14 @@ Stopped `notmuch dump` failing if someone writes to the database limits output of message IDs to messages matching search terms that have at least `N` files associated with them. +Added `notmuch address` subcommand + + This new subcommand searches for messages matching the given search + terms, and prints the addresses from them. Duplicate addresses are + filtered out. The `--output` option controls which of the following + information is printed: sender addresses, recipient addresses and + count of duplicate addresses. + Emacs Interface --- -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/2] doc: Minor fixes related to notmuch-address
- Add notmuch-address to HTML index - Remove extra '*' --- doc/index.rst| 1 + doc/man1/notmuch.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index ba6d5b4..3f0e6e6 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -8,6 +8,7 @@ Contents: :titlesonly: man1/notmuch + man1/notmuch-address man1/notmuch-compact man1/notmuch-config man1/notmuch-count diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst index 98590a4..b33738e 100644 --- a/doc/man1/notmuch.rst +++ b/doc/man1/notmuch.rst @@ -129,7 +129,7 @@ SEE ALSO **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**, **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**, -***notmuch-address(1)** +**notmuch-address(1)** The notmuch website: **http://notmuchmail.org** -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] cli: notmuch address option defaults update
On Thu, Nov 06 2014, Tomi Ollila wrote: > When no --output option were given, change default to display senders > only. This is faster and provides useful-enough list of addresses. > > When only --count option is given, display senders (in contrary to not > displaying anything). > > Document how --count affects to "sort order" a bit more accurately. > > Clean up some whitespace in the documentation. > > One test updated to have --output=count without sender nor recipient > output option. > --- > > Some quick updates to the notmuch address interface which I hope will > be considered to be included in 0.19 release. As we are in feature freeze > I hope this gets quick feedback, in any way you desire. > > Tomi > > doc/man1/notmuch-address.rst | 34 ++ > notmuch-search.c | 4 ++-- > test/T095-address.sh | 14 +++--- > 3 files changed, 27 insertions(+), 25 deletions(-) > > diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst > index 359616e0dc5f..034607c434d2 100644 > --- a/doc/man1/notmuch-address.rst > +++ b/doc/man1/notmuch-address.rst > @@ -32,28 +32,28 @@ Supported options for **address** include > ``--output=(sender|recipients|count)`` > > Controls which information appears in the output. This option > - can be given multiple times to combine different outputs. > - Omitting this option is equivalent to > - --output=sender --output=recipients. > +can be given multiple times to combine different outputs. > +Omitting this option is equivalent to --output=sender. The implementation does a bit more. What about: When neither --output=sender nor --output=recipients is given, --output=sender is implied. Otherwise it LGTM. Thanks, -Michal
Re: [PATCH] cli: notmuch address option defaults update
On Thu, Nov 06 2014, Tomi Ollila wrote: When no --output option were given, change default to display senders only. This is faster and provides useful-enough list of addresses. When only --count option is given, display senders (in contrary to not displaying anything). Document how --count affects to sort order a bit more accurately. Clean up some whitespace in the documentation. One test updated to have --output=count without sender nor recipient output option. --- Some quick updates to the notmuch address interface which I hope will be considered to be included in 0.19 release. As we are in feature freeze I hope this gets quick feedback, in any way you desire. Tomi doc/man1/notmuch-address.rst | 34 ++ notmuch-search.c | 4 ++-- test/T095-address.sh | 14 +++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 359616e0dc5f..034607c434d2 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -32,28 +32,28 @@ Supported options for **address** include ``--output=(sender|recipients|count)`` Controls which information appears in the output. This option - can be given multiple times to combine different outputs. - Omitting this option is equivalent to - --output=sender --output=recipients. +can be given multiple times to combine different outputs. +Omitting this option is equivalent to --output=sender. The implementation does a bit more. What about: When neither --output=sender nor --output=recipients is given, --output=sender is implied. Otherwise it LGTM. Thanks, -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 06/10] cli: Introduce "notmuch address" command
On Wed, Nov 05 2014, Mark Walters wrote: > On Tue, 04 Nov 2014, Michal Sojka wrote: >> On Tue, Nov 04 2014, Mark Walters wrote: >>> On Mon, 03 Nov 2014, Michal Sojka wrote: >>>> This moves address-related functionality from search command to the >>>> new address command. The implementation shares almost all code and >>>> some command line options. >>>> >>>> Options --offset and --limit were intentionally not included in the >>>> address command, because they refer to messages numbers, which users >>>> do not see in the output. This could confuse users because, for >>>> example, they could see more addresses in the output that what was >>>> specified with --limit. This functionality can be correctly >>>> reimplemented for addresses later. >>> >>> I am not sure about this: we already have this anomaly for output=files >>> say. Also I can imagine calling notmuch address --limit=1000 ... to get >>> a bunch of recent addresses quickly and I really am wanting to look at >>> 1000 messages, not collect 1000 addresses. >> >> I think that one of the reasons for having the new "address" command is >> to have cleaner user interface. And including "anomalies" doesn't sound >> like a way to achieve this. I think that now you can use "date:" query >> to limit the search. >> >> I volunteer to implement "address --limit" properly after 0.19. This >> should be easy. > > I think this depends on how you view limit: is it to limit the output > (roughly to run "head" on the output), or is to bound the amount of work > notmuch has to do (eg to make sure you don't get a long delay). Your > suggestion is definitely the former, whereas I am more worried about the > latter: limit in your definition could take an essentially unbounded > amount of time. Why? If I understand you correctly, you think of limit in terms of messages. There is 1:N mapping between messages and addresses, where N?>=?1. If I limit the number of printed addresses, I limit the number of messages as well. Only if N is zero (which probably can be the case with Bcc and --output=recipients) then it can result in unbounded work (provided you have infinite number of Bcc only messages in your database?:-)). Do I miss something? -Michal
[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
This option allows to configure the criterion for duplicate address filtering. Without this option, all unique combinations of name and address parts are printed. This option allows to filter the output more, for example to only contain unique address parts. --- completion/notmuch-completion.bash | 6 +++- completion/notmuch-completion.zsh | 1 + doc/man1/notmuch-address.rst | 36 ++- notmuch-search.c | 51 -- test/T097-address-filter-by.sh | 73 ++ 5 files changed, 162 insertions(+), 5 deletions(-) create mode 100755 test/T097-address-filter-by.sh diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index db152f3..2cb1586 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -310,7 +310,7 @@ _notmuch_search() ! $split && case "${cur}" in -*) - local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate=" + local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate= --filter-by=" compopt -o nospace COMPREPLY=( $(compgen -W "$options" -- ${cur}) ) ;; @@ -343,6 +343,10 @@ _notmuch_address() COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) ) return ;; + --filter-by) + COMPREPLY=( $( compgen -W "nameaddr name addr addrfold nameaddrfold" -- "${cur}" ) ) + return + ;; esac ! $split && diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 8968562..3758f1a 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -62,6 +62,7 @@ _notmuch_address() _arguments -s : \ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ '--output=[select what to output]:output:((sender recipients count))' +'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both name and address part" name\:"name part" addr\:"address part" addrfold\:"case-insensitive address part" nameaddrfold\:"name and case-insensitive address part"))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 359616e..00582c3 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -11,7 +11,8 @@ DESCRIPTION === Search for messages matching the given search terms, and display the -addresses from them. Duplicate addresses are filtered out. +addresses from them. Duplicate addresses are filtered out. Filtering +can be configured with the --filter-by option. See **notmuch-search-terms(7)** for details of the supported syntax for . @@ -77,6 +78,39 @@ Supported options for **address** include **false** allows excluded messages to match search terms and appear in displayed results. +``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ **addrfold**\ \|\ **nameaddrfold**\) + + Controls how to filter out duplicate addresses. The filtering + algorithm receives a sequence of email addresses and outputs + the same sequence without the addresses that are considered a + duplicate of a previously output address. What is considered a + duplicate depends on how the two addresses are compared: + + **nameaddr** means that both name and address parts are + compared in case-sensitive manner. Therefore, all same looking + addresses strings are considered duplicate. This is the + default. + + **name** means that only the name part is compared (in + case-sensitive manner). For example, the addresses "John Doe + " and "John Doe " will be + considered duplicate. + + **addr** means that only the address part is compared (in + case-sensitive manner). For example, the addresses "John Doe + " and "Dr. John Doe " will + be considered duplicate. + + **addrfold** is like **addr**, but comparison is done in + canse-insensitive manner. For example, the addresses "John Doe + " and "Dr. John Doe " will + be considered duplicate. + + **nameaddrfold** is like **nameaddr**, but address comparison + is done in canse-insensitive manner. For example, the + addresses "John Doe " and "John Doe + " will be considered duplicate. + EXIT STATUS === diff --git a/notmuch-search.c b/notmuch-search.c index 5036d8e..246ec0a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -43,6 +43,14 @@ typedef enum { NOTMUCH_FORMAT_SEXP } format_sel_t; +typedef enum { +FILTER_BY_NAMEADDR = 0, +FILTER_BY_NAME, +FILTER_BY_ADDR, +FILTER_BY_ADDRFOLD, +FILTER_BY_NAMEADDRFOLD, +} filter_by_t; + typedef struct { notmuch_database_t *notmuch; format_sel_t format_sel;
[PATCH v3 09/10] cli: address: Add --output=count
This output prints how many times was each address encountered during search. --- completion/notmuch-completion.bash | 2 +- completion/notmuch-completion.zsh | 2 +- doc/man1/notmuch-address.rst | 11 - notmuch-search.c | 49 -- test/T095-address.sh | 49 ++ 5 files changed, 103 insertions(+), 10 deletions(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 94ea2d5..db152f3 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -332,7 +332,7 @@ _notmuch_address() return ;; --output) - COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) + COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) ) return ;; --sort) diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index c606b75..8968562 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -61,7 +61,7 @@ _notmuch_address() { _arguments -s : \ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ -'--output=[select what to output]:output:((sender recipients))' +'--output=[select what to output]:output:((sender recipients count))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 01eb811..359616e 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -29,7 +29,7 @@ Supported options for **address** include intended for programs that invoke **notmuch(1)** internally. If omitted, the latest supported version will be used. -``--output=(sender|recipients)`` +``--output=(sender|recipients|count)`` Controls which information appears in the output. This option can be given multiple times to combine different outputs. @@ -48,6 +48,13 @@ Supported options for **address** include Output all addresses from the *To*, *Cc* and *Bcc* headers. + **count** + Print the count of how many times was the address + encountered during search. + + Note: With this option, addresses are printed only after + the whole search is finished. This may take long time. + ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological @@ -56,6 +63,8 @@ Supported options for **address** include By default, results will be displayed in reverse chronological order, (that is, the newest results will be displayed first). + This option has no effect when used with --output=count. + ``--exclude=(true|false)`` A message is called "excluded" if it matches at least one tag in search.tag\_exclude that does not appear explicitly in the diff --git a/notmuch-search.c b/notmuch-search.c index 86d54ba..5036d8e 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -33,6 +33,7 @@ typedef enum { /* Address command */ OUTPUT_SENDER = 1 << 5, OUTPUT_RECIPIENTS = 1 << 6, +OUTPUT_COUNT = 1 << 7, } output_t; typedef enum { @@ -59,6 +60,7 @@ typedef struct { typedef struct { const char *name; const char *addr; +int count; } mailbox_t; /* Return two stable query strings that identify exactly the matched @@ -248,17 +250,24 @@ is_duplicate (const search_context_t *ctx, const char *name, const char *addr) { notmuch_bool_t duplicate; char *key; +mailbox_t *mailbox; key = talloc_asprintf (ctx->format, "%s <%s>", name, addr); if (! key) return FALSE; -duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL); +duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, (gpointer)); -if (! duplicate) - g_hash_table_insert (ctx->addresses, key, NULL); -else +if (! duplicate) { + mailbox = talloc (ctx->format, mailbox_t); + mailbox->name = talloc_strdup (mailbox, name); + mailbox->addr = talloc_strdup (mailbox, addr); + mailbox->count = 1; + g_hash_table_insert (ctx->addresses, key, mailbox); +} else { + mailbox->count++; talloc_free (key); +} return duplicate; } @@ -268,6 +277,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { const char *name = mailbox->name; const char *addr = mailbox->addr; +int count = mailbox->count; sprinter_t *format = ctx->format; InternetAddress *ia = internet_address_mailbox_new (name, addr); char *name_addr; @@ -277,6 +287,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) name_addr =
[PATCH v3 08/10] cli: address: Do not output duplicate addresses
This filters out duplicate addresses from address command output. It also also adds tests for the address command. The code here is an extended version of a patch from Jani Nikula. --- doc/man1/notmuch-address.rst | 2 +- notmuch-search.c | 42 ++- test/T095-address.sh | 99 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100755 test/T095-address.sh diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index d349237..01eb811 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -11,7 +11,7 @@ DESCRIPTION === Search for messages matching the given search terms, and display the -addresses from them. +addresses from them. Duplicate addresses are filtered out. See **notmuch-search-terms(7)** for details of the supported syntax for . diff --git a/notmuch-search.c b/notmuch-search.c index e084d25..86d54ba 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -53,6 +53,7 @@ typedef struct { int offset; int limit; int dupe; +GHashTable *addresses; } search_context_t; typedef struct { @@ -240,6 +241,28 @@ do_search_threads (search_context_t *ctx) return 0; } +/* Returns TRUE iff name and addr is duplicate. If not, stores the + * name/addr pair in order to detect subsequent duplicates. */ +static notmuch_bool_t +is_duplicate (const search_context_t *ctx, const char *name, const char *addr) +{ +notmuch_bool_t duplicate; +char *key; + +key = talloc_asprintf (ctx->format, "%s <%s>", name, addr); +if (! key) + return FALSE; + +duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL); + +if (! duplicate) + g_hash_table_insert (ctx->addresses, key, NULL); +else + talloc_free (key); + +return duplicate; +} + static void print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { @@ -274,7 +297,8 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) /* Print addresses from InternetAddressList. */ static void -process_address_list (const search_context_t *ctx, InternetAddressList *list) +process_address_list (const search_context_t *ctx, + InternetAddressList *list) { InternetAddress *address; int i; @@ -298,6 +322,9 @@ process_address_list (const search_context_t *ctx, InternetAddressList *list) .addr = internet_address_mailbox_get_addr (mailbox), }; + if (is_duplicate (ctx, mbx.name, mbx.addr)) + continue; + print_mailbox (ctx, ); } } @@ -321,6 +348,13 @@ process_address_header (const search_context_t *ctx, const char *value) g_object_unref (list); } +/* Destructor for talloc-allocated GHashTable keys and values. */ +static void +_talloc_free_for_g_hash (void *ptr) +{ +talloc_free (ptr); +} + static int _count_filenames (notmuch_message_t *message) { @@ -673,8 +707,14 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; +ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal, + _talloc_free_for_g_hash, NULL); + ret = do_search_messages (ctx); +g_hash_table_unref (ctx->addresses); + + _notmuch_search_cleanup (ctx); return ret ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/test/T095-address.sh b/test/T095-address.sh new file mode 100755 index 000..0d47c0d --- /dev/null +++ b/test/T095-address.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +test_description='"notmuch address" in several variants' +. ./test-lib.sh + +add_email_corpus + +test_begin_subtest "--output=sender" +notmuch address --output=sender '*' >OUTPUT +catOUTPUT +cat
[PATCH v3 07/10] cli: search: Convert --output to keyword argument
Now, when address related outputs are in a separate command, it makes no sense to combine multiple --output options in search command line. Using switch statement to handle different outputs is more readable than a series of if statements. --- doc/man1/notmuch-search.rst | 3 --- notmuch-search.c| 25 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst index 65df288..0cc2911 100644 --- a/doc/man1/notmuch-search.rst +++ b/doc/man1/notmuch-search.rst @@ -78,9 +78,6 @@ Supported options for **search** include by null characters (--format=text0), as a JSON array (--format=json), or as an S-Expression list (--format=sexp). - This option can be given multiple times to combine different - outputs. - ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological diff --git a/notmuch-search.c b/notmuch-search.c index 69938d6..e084d25 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -587,7 +587,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) int opt_index, ret; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o', + { NOTMUCH_OPT_KEYWORD, >output, "output", 'o', (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, { "threads", OUTPUT_THREADS }, { "messages", OUTPUT_MESSAGES }, @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0, 0, 0, 0 } }; +ctx->output = OUTPUT_SUMMARY; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) return EXIT_FAILURE; -if (! ctx->output) - ctx->output = OUTPUT_SUMMARY; - if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES && ctx->dupe != -1) { fprintf (stderr, "Error: --duplicate=N is only supported with --output=files and --output=messages.\n"); @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -if (ctx->output == OUTPUT_SUMMARY || - ctx->output == OUTPUT_THREADS) +switch (ctx->output) { +case OUTPUT_SUMMARY: +case OUTPUT_THREADS: ret = do_search_threads (ctx); -else if (ctx->output == OUTPUT_MESSAGES || -ctx->output == OUTPUT_FILES) + break; +case OUTPUT_MESSAGES: +case OUTPUT_FILES: ret = do_search_messages (ctx); -else if (ctx->output == OUTPUT_TAGS) + break; +case OUTPUT_TAGS: ret = do_search_tags (ctx); -else { - fprintf (stderr, "Error: the combination of outputs is not supported.\n"); - ret = 1; + break; +default: + INTERNAL_ERROR ("Unexpected output"); } _notmuch_search_cleanup (ctx); -- 2.1.1
[PATCH v3 06/10] cli: Introduce "notmuch address" command
This moves address-related functionality from search command to the new address command. The implementation shares almost all code and some command line options. Options --offset and --limit were intentionally not included in the address command, because they refer to messages numbers, which users do not see in the output. This could confuse users because, for example, they could see more addresses in the output that what was specified with --limit. This functionality can be correctly reimplemented for address subcommand later. Also useless values of --exclude flag were not included in the address command. This was inspired by a patch from Jani Nikula. --- completion/notmuch-completion.bash | 42 - completion/notmuch-completion.zsh | 10 +++- doc/man1/notmuch-address.rst | 89 doc/man1/notmuch-search.rst| 20 +--- doc/man1/notmuch.rst | 7 +-- notmuch-client.h | 3 ++ notmuch-search.c | 93 +++--- notmuch.c | 2 + 8 files changed, 216 insertions(+), 50 deletions(-) create mode 100644 doc/man1/notmuch-address.rst diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cfbd389..94ea2d5 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) - COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) ) + COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) ) return ;; --sort) @@ -320,6 +320,44 @@ _notmuch_search() esac } +_notmuch_address() +{ +local cur prev words cword split +_init_completion -s || return + +$split && +case "${prev}" in + --format) + COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) ) + return + ;; + --output) + COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) + return + ;; + --sort) + COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) ) + return + ;; + --exclude) + COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) ) + return + ;; +esac + +! $split && +case "${cur}" in + -*) + local options="--format= --output= --sort= --exclude=" + compopt -o nospace + COMPREPLY=( $(compgen -W "$options" -- ${cur}) ) + ;; + *) + _notmuch_search_terms + ;; +esac +} + _notmuch_show() { local cur prev words cword split @@ -393,7 +431,7 @@ _notmuch_tag() _notmuch() { -local _notmuch_commands="compact config count dump help insert new reply restore search setup show tag" +local _notmuch_commands="compact config count dump help insert new reply restore search address setup show tag" local arg cur prev words cword split # require bash-completion with _init_completion diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 3e52a00..c606b75 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -10,6 +10,7 @@ _notmuch_commands() 'setup:interactively set up notmuch for first use' 'new:find and import any new message to the database' 'search:search for messages matching the search terms, display matching threads as results' +'address:get addresses from messages matching the given search terms' 'reply:constructs a reply template for a set of messages' 'show:show all messages matching the search terms' 'tag:add or remove tags for all messages matching the search terms' @@ -53,7 +54,14 @@ _notmuch_search() '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ -'--output=[select what to output]:output:((summary threads messages files tags sender recipients))' +'--output=[select what to output]:output:((summary threads messages files tags))' +} + +_notmuch_address() +{ + _arguments -s : \ +'--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ +'--output=[select what to output]:output:((sender recipients))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst new file mode 100644 index 000..d349237 --- /dev/null +++ b/doc/man1/notmuch-address.rst @@ -0,0 +1,89 @@ +=== +notmuch-address +=== + +SYNOPSIS +
[PATCH v3 05/10] cli: add support for hierarchical command line option arrays
From: Jani NikulaNOTMUCH_OPT_INHERIT expects a notmuch_opt_desc_t * pointer in output_var. The "Unrecognized option" message was moved out of parse_option() to not be emitted twice or when parsing a non-inherited option. --- command-line-arguments.c | 16 +--- command-line-arguments.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/command-line-arguments.c b/command-line-arguments.c index c6f7269..de6b453 100644 --- a/command-line-arguments.c +++ b/command-line-arguments.c @@ -122,16 +122,18 @@ parse_position_arg (const char *arg_str, int pos_arg_index, */ notmuch_bool_t -parse_option (const char *arg, - const notmuch_opt_desc_t *options) { - -assert(arg); +parse_option (const char *_arg, const notmuch_opt_desc_t *options) +{ +assert(_arg); assert(options); -arg += 2; - +const char *arg = _arg + 2; /* _arg starts with -- */ const notmuch_opt_desc_t *try; for (try = options; try->opt_type != NOTMUCH_OPT_END; try++) { + if (try->opt_type == NOTMUCH_OPT_INHERIT && + parse_option (_arg, try->output_var)) + return TRUE; + if (! try->name) continue; @@ -170,7 +172,6 @@ parse_option (const char *arg, /*UNREACHED*/ } } -fprintf (stderr, "Unrecognized option: --%s\n", arg); return FALSE; } @@ -201,6 +202,7 @@ parse_arguments (int argc, char **argv, if (more_args) { opt_index++; } else { + fprintf (stderr, "Unrecognized option: %s\n", argv[opt_index]); opt_index = -1; } diff --git a/command-line-arguments.h b/command-line-arguments.h index 6444129..309aaf2 100644 --- a/command-line-arguments.h +++ b/command-line-arguments.h @@ -5,6 +5,7 @@ enum notmuch_opt_type { NOTMUCH_OPT_END = 0, +NOTMUCH_OPT_INHERIT, /* another options table */ NOTMUCH_OPT_BOOLEAN, /* --verbose */ NOTMUCH_OPT_INT, /* --frob=8 */ NOTMUCH_OPT_KEYWORD, /* --format=raw|json|text */ -- 2.1.1
[PATCH v3 04/10] cli: search: Split notmuch_search_command to smaller functions
In a subsequent commit, these functions will be used to share some functionality between search and address commands. --- notmuch-search.c | 155 ++- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6765a16..f115359 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx) return 0; } +static int +_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int argc, char *argv[]) +{ +char *query_str; +unsigned int i; + +switch (ctx->format_sel) { +case NOTMUCH_FORMAT_TEXT: + ctx->format = sprinter_text_create (config, stdout); + break; +case NOTMUCH_FORMAT_TEXT0: + if (ctx->output == OUTPUT_SUMMARY) { + fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); + return EXIT_FAILURE; + } + ctx->format = sprinter_text0_create (config, stdout); + break; +case NOTMUCH_FORMAT_JSON: + ctx->format = sprinter_json_create (config, stdout); + break; +case NOTMUCH_FORMAT_SEXP: + ctx->format = sprinter_sexp_create (config, stdout); + break; +default: + /* this should never happen */ + INTERNAL_ERROR("no output format selected"); +} + +notmuch_exit_if_unsupported_format (); + +if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch)) + return EXIT_FAILURE; + +query_str = query_string_from_args (ctx->notmuch, argc, argv); +if (query_str == NULL) { + fprintf (stderr, "Out of memory.\n"); + return EXIT_FAILURE; +} +if (*query_str == '\0') { + fprintf (stderr, "Error: notmuch search requires at least one search term.\n"); + return EXIT_FAILURE; +} + +ctx->query = notmuch_query_create (ctx->notmuch, query_str); +if (ctx->query == NULL) { + fprintf (stderr, "Out of memory\n"); + return EXIT_FAILURE; +} + +notmuch_query_set_sort (ctx->query, ctx->sort); + +if (ctx->exclude == NOTMUCH_EXCLUDE_FLAG && ctx->output != OUTPUT_SUMMARY) { + /* If we are not doing summary output there is nowhere to +* print the excluded flag so fall back on including the +* excluded messages. */ + fprintf (stderr, "Warning: this output format cannot flag excluded messages.\n"); + ctx->exclude = NOTMUCH_EXCLUDE_FALSE; +} + +if (ctx->exclude != NOTMUCH_EXCLUDE_FALSE) { + const char **search_exclude_tags; + size_t search_exclude_tags_length; + + search_exclude_tags = notmuch_config_get_search_exclude_tags + (config, _exclude_tags_length); + for (i = 0; i < search_exclude_tags_length; i++) + notmuch_query_add_tag_exclude (ctx->query, search_exclude_tags[i]); + notmuch_query_set_omit_excluded (ctx->query, ctx->exclude); +} + +return 0; +} + +static void +_notmuch_search_cleanup (search_context_t *ctx) +{ +notmuch_query_destroy (ctx->query); +notmuch_database_destroy (ctx->notmuch); + +talloc_free (ctx->format); +} + int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { @@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .dupe = -1, }; search_context_t *ctx = _context; -char *query_str; int opt_index, ret; -unsigned int i; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, >sort, "sort", 's', @@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (ctx->format_sel) { -case NOTMUCH_FORMAT_TEXT: - ctx->format = sprinter_text_create (config, stdout); - break; -case NOTMUCH_FORMAT_TEXT0: - if (ctx->output == OUTPUT_SUMMARY) { - fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); - return EXIT_FAILURE; - } - ctx->format = sprinter_text0_create (config, stdout); - break; -case NOTMUCH_FORMAT_JSON: - ctx->format = sprinter_json_create (config, stdout); - break; -case NOTMUCH_FORMAT_SEXP: - ctx->format = sprinter_sexp_create (config, stdout); - break; -default: - /* this should never happen */ - INTERNAL_ERROR("no output format selected"); -} - -notmuch_exit_if_unsupported_format (); - -if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch)) +if (_notmuch_search_prepare (ctx, config, +argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -query_str = query_string_from_args (ctx->notmuch, argc-opt_index, argv+opt_index); -if
[PATCH v3 03/10] cli: search: Convert ctx. to ctx->
In the next commit, notmuch_search_command will be refactored to several smaller functions. In order to simplify the next commit to verbatim move of several lines to new functions with search_context_t* argument, we convert all references to this structure to pointer dereferences. To do so we rename the context variable and use the original name ctx as the pointer to the renamed structure. --- notmuch-search.c | 81 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 3d2012b..6765a16 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -474,7 +474,7 @@ do_search_tags (const search_context_t *ctx) int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -search_context_t ctx = { +search_context_t search_context = { .format_sel = NOTMUCH_FORMAT_TEXT, .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, @@ -483,23 +483,24 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .limit = -1, /* unlimited */ .dupe = -1, }; +search_context_t *ctx = _context; char *query_str; int opt_index, ret; unsigned int i; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD, , "sort", 's', + { NOTMUCH_OPT_KEYWORD, >sort, "sort", 's', (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST }, { "newest-first", NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f', + { NOTMUCH_OPT_KEYWORD, >format_sel, "format", 'f', (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, { "sexp", NOTMUCH_FORMAT_SEXP }, { "text", NOTMUCH_FORMAT_TEXT }, { "text0", NOTMUCH_FORMAT_TEXT0 }, { 0, 0 } } }, { NOTMUCH_OPT_INT, _format_version, "format-version", 0, 0 }, - { NOTMUCH_OPT_KEYWORD_FLAGS, , "output", 'o', + { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o', (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, { "threads", OUTPUT_THREADS }, { "messages", OUTPUT_MESSAGES }, @@ -508,15 +509,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { "files", OUTPUT_FILES }, { "tags", OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x', +{ NOTMUCH_OPT_KEYWORD, >exclude, "exclude", 'x', (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE }, { "false", NOTMUCH_EXCLUDE_FALSE }, { "flag", NOTMUCH_EXCLUDE_FLAG }, { "all", NOTMUCH_EXCLUDE_ALL }, { 0, 0 } } }, - { NOTMUCH_OPT_INT, , "offset", 'O', 0 }, - { NOTMUCH_OPT_INT, , "limit", 'L', 0 }, - { NOTMUCH_OPT_INT, , "duplicate", 'D', 0 }, + { NOTMUCH_OPT_INT, >offset, "offset", 'O', 0 }, + { NOTMUCH_OPT_INT, >limit, "limit", 'L', 0 }, + { NOTMUCH_OPT_INT, >dupe, "duplicate", 'D', 0 }, { 0, 0, 0, 0, 0 } }; @@ -524,31 +525,31 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; -if (! ctx.output) - ctx.output = OUTPUT_SUMMARY; +if (! ctx->output) + ctx->output = OUTPUT_SUMMARY; -if (ctx.output != OUTPUT_FILES && ctx.output != OUTPUT_MESSAGES && - ctx.dupe != -1) { +if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES && + ctx->dupe != -1) { fprintf (stderr, "Error: --duplicate=N is only supported with --output=files and --output=messages.\n"); return EXIT_FAILURE; } -switch (ctx.format_sel) { +switch (ctx->format_sel) { case NOTMUCH_FORMAT_TEXT: - ctx.format = sprinter_text_create (config, stdout); + ctx->format = sprinter_text_create (config, stdout); break; case NOTMUCH_FORMAT_TEXT0: - if (ctx.output == OUTPUT_SUMMARY) { + if (ctx->output == OUTPUT_SUMMARY) { fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); return EXIT_FAILURE; } - ctx.format = sprinter_text0_create (config, stdout); + ctx->format = sprinter_text0_create (config, stdout); break; case NOTMUCH_FORMAT_JSON: - ctx.format = sprinter_json_create (config, stdout); + ctx->format = sprinter_json_create (config, stdout); break; case NOTMUCH_FORMAT_SEXP: - ctx.format = sprinter_sexp_create (config, stdout); +
[PATCH v3 02/10] cli: search: Move more variables into search_context_t
In order to share some command line options between search and address subcommands we need to add corresponding variables to the context structure. While we are at it, we also add notmuch_database_t to unify parameters of all do_search_* functions and to simplify subsequent commits. Otherwise, there are no functional changes. --- notmuch-search.c | 49 ++--- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 2c47b80..3d2012b 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -34,8 +34,18 @@ typedef enum { #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS) +typedef enum { +NOTMUCH_FORMAT_JSON, +NOTMUCH_FORMAT_TEXT, +NOTMUCH_FORMAT_TEXT0, +NOTMUCH_FORMAT_SEXP +} format_sel_t; + typedef struct { +notmuch_database_t *notmuch; +format_sel_t format_sel; sprinter_t *format; +notmuch_exclude_t exclude; notmuch_query_t *query; notmuch_sort_t sort; output_t output; @@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx) } static int -do_search_tags (notmuch_database_t *notmuch, - const search_context_t *ctx) +do_search_tags (const search_context_t *ctx) { notmuch_messages_t *messages = NULL; notmuch_tags_t *tags; const char *tag; sprinter_t *format = ctx->format; notmuch_query_t *query = ctx->query; +notmuch_database_t *notmuch = ctx->notmuch; /* should the following only special case if no excluded terms * specified? */ @@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch, int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -notmuch_database_t *notmuch; search_context_t ctx = { + .format_sel = NOTMUCH_FORMAT_TEXT, + .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, .output = 0, .offset = 0, @@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) }; char *query_str; int opt_index, ret; -notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE; unsigned int i; -enum { - NOTMUCH_FORMAT_JSON, - NOTMUCH_FORMAT_TEXT, - NOTMUCH_FORMAT_TEXT0, - NOTMUCH_FORMAT_SEXP -} format_sel = NOTMUCH_FORMAT_TEXT; - notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, , "sort", 's', (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST }, { "newest-first", NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f', + { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f', (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, { "sexp", NOTMUCH_FORMAT_SEXP }, { "text", NOTMUCH_FORMAT_TEXT }, @@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { "files", OUTPUT_FILES }, { "tags", OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x', +{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x', (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE }, { "false", NOTMUCH_EXCLUDE_FALSE }, { "flag", NOTMUCH_EXCLUDE_FLAG }, @@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (format_sel) { +switch (ctx.format_sel) { case NOTMUCH_FORMAT_TEXT: ctx.format = sprinter_text_create (config, stdout); break; @@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_exit_if_unsupported_format (); if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, )) + NOTMUCH_DATABASE_MODE_READ_ONLY, )) return EXIT_FAILURE; -query_str = query_string_from_args (notmuch, argc-opt_index, argv+opt_index); +query_str = query_string_from_args (ctx.notmuch, argc-opt_index, argv+opt_index); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return EXIT_FAILURE; @@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -ctx.query = notmuch_query_create (notmuch, query_str); +ctx.query = notmuch_query_create (ctx.notmuch, query_str); if (ctx.query == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; @@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_query_set_sort (ctx.query, ctx.sort); -if
[PATCH v3 01/10] cli: search: Rename options to context
In the next commit the options structure will be extended by non-option variables. Therefore we need a more generic name. Just text replacement, no other changes. --- notmuch-search.c | 142 +++ 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6345fb6..2c47b80 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -42,7 +42,7 @@ typedef struct { int offset; int limit; int dupe; -} search_options_t; +} search_context_t; typedef struct { const char *name; @@ -89,39 +89,39 @@ get_thread_query (notmuch_thread_t *thread, } static int -do_search_threads (search_options_t *opt) +do_search_threads (search_context_t *ctx) { notmuch_thread_t *thread; notmuch_threads_t *threads; notmuch_tags_t *tags; -sprinter_t *format = opt->format; +sprinter_t *format = ctx->format; time_t date; int i; -if (opt->offset < 0) { - opt->offset += notmuch_query_count_threads (opt->query); - if (opt->offset < 0) - opt->offset = 0; +if (ctx->offset < 0) { + ctx->offset += notmuch_query_count_threads (ctx->query); + if (ctx->offset < 0) + ctx->offset = 0; } -threads = notmuch_query_search_threads (opt->query); +threads = notmuch_query_search_threads (ctx->query); if (threads == NULL) return 1; format->begin_list (format); for (i = 0; -notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset + opt->limit); +notmuch_threads_valid (threads) && (ctx->limit < 0 || i < ctx->offset + ctx->limit); notmuch_threads_move_to_next (threads), i++) { thread = notmuch_threads_get (threads); - if (i < opt->offset) { + if (i < ctx->offset) { notmuch_thread_destroy (thread); continue; } - if (opt->output == OUTPUT_THREADS) { + if (ctx->output == OUTPUT_THREADS) { format->set_prefix (format, "thread"); format->string (format, notmuch_thread_get_thread_id (thread)); @@ -138,7 +138,7 @@ do_search_threads (search_options_t *opt) format->begin_map (format); - if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST) + if (ctx->sort == NOTMUCH_SORT_OLDEST_FIRST) date = notmuch_thread_get_oldest_date (thread); else date = notmuch_thread_get_newest_date (thread); @@ -230,11 +230,11 @@ do_search_threads (search_options_t *opt) } static void -print_mailbox (const search_options_t *opt, const mailbox_t *mailbox) +print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { const char *name = mailbox->name; const char *addr = mailbox->addr; -sprinter_t *format = opt->format; +sprinter_t *format = ctx->format; InternetAddress *ia = internet_address_mailbox_new (name, addr); char *name_addr; @@ -263,7 +263,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t *mailbox) /* Print addresses from InternetAddressList. */ static void -process_address_list (const search_options_t *opt, InternetAddressList *list) +process_address_list (const search_context_t *ctx, InternetAddressList *list) { InternetAddress *address; int i; @@ -279,7 +279,7 @@ process_address_list (const search_options_t *opt, InternetAddressList *list) if (group_list == NULL) continue; - process_address_list (opt, group_list); + process_address_list (ctx, group_list); } else { InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address); mailbox_t mbx = { @@ -287,14 +287,14 @@ process_address_list (const search_options_t *opt, InternetAddressList *list) .addr = internet_address_mailbox_get_addr (mailbox), }; - print_mailbox (opt, ); + print_mailbox (ctx, ); } } } /* Print addresses from a message header. */ static void -process_address_header (const search_options_t *opt, const char *value) +process_address_header (const search_context_t *ctx, const char *value) { InternetAddressList *list; @@ -305,7 +305,7 @@ process_address_header (const search_options_t *opt, const char *value) if (list == NULL) return; -process_address_list (opt, list); +process_address_list (ctx, list); g_object_unref (list); } @@ -329,36 +329,36 @@ _count_filenames (notmuch_message_t *message) } static int -do_search_messages (search_options_t *opt) +do_search_messages (search_context_t *ctx) { notmuch_message_t *message; notmuch_messages_t *messages; notmuch_filenames_t *filenames; -sprinter_t *format = opt->format; +sprinter_t *format = ctx->format; int i; -if (opt->offset < 0) { - opt->offset += notmuch_query_count_messages (opt->query); - if
[PATCH v3 00/10] "notmuch address" command
Hi all, this is v3 of "notmuch address" patchset. It obsoletes [1]. I think I addressed all comments made to v2. The diff between v2 and v3 is below. Besides this, I improved commit messages. I also tried to get rid of global variables in 6/10, but it looked ugly, because the definition of keywords (e.g. "text", "json", ...) had to be outside of option definition. So I kept the code as it was in v2. It seems that the agreement is to merge patches 1-9 for 0.19 and leave patch 10 for further discussion. Thanks -Michal [1] id:1415058622-21162-1-git-send-email-sojkam1 at fel.cvut.cz Jani Nikula (1): cli: add support for hierarchical command line option arrays Michal Sojka (9): cli: search: Rename options to context cli: search: Move more variables into search_context_t cli: search: Convert ctx. to ctx-> cli: search: Split notmuch_search_command to smaller functions cli: Introduce "notmuch address" command cli: search: Convert --output to keyword argument cli: address: Do not output duplicate addresses cli: address: Add --output=count cli: address: Add --filter-by option to configure address filtering command-line-arguments.c | 16 +- command-line-arguments.h | 1 + completion/notmuch-completion.bash | 48 +++- completion/notmuch-completion.zsh | 11 +- doc/man1/notmuch-address.rst | 132 +++ doc/man1/notmuch-search.rst| 21 +- doc/man1/notmuch.rst | 7 +- notmuch-client.h | 3 + notmuch-search.c | 461 ++--- notmuch.c | 2 + test/T095-address.sh | 148 test/T097-address-filter-by.sh | 73 ++ 12 files changed, 750 insertions(+), 173 deletions(-) create mode 100644 doc/man1/notmuch-address.rst create mode 100755 test/T095-address.sh create mode 100755 test/T097-address-filter-by.sh -- 2.1.1 diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 524ab91..00582c3 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -30,7 +30,7 @@ Supported options for **address** include intended for programs that invoke **notmuch(1)** internally. If omitted, the latest supported version will be used. -``--output=(sender|recipients)`` +``--output=(sender|recipients|count)`` Controls which information appears in the output. This option can be given multiple times to combine different outputs. @@ -64,7 +64,9 @@ Supported options for **address** include By default, results will be displayed in reverse chronological order, (that is, the newest results will be displayed first). -``--exclude=(true|false|all|flag)`` + This option has no effect when used with --output=count. + +``--exclude=(true|false)`` A message is called "excluded" if it matches at least one tag in search.tag\_exclude that does not appear explicitly in the search terms. This option specifies whether to omit excluded @@ -73,18 +75,8 @@ Supported options for **address** include The default value, **true**, prevents excluded messages from matching the search terms. -**all** additionally prevents excluded messages from appearing -in displayed results, in effect behaving as though the excluded -messages do not exist. - **false** allows excluded messages to match search terms and -appear in displayed results. Excluded messages are still marked -in the relevant outputs. - -**flag** only has an effect when ``--output=summary``. The -output is almost identical to **false**, but the "match count" -is the number of matching non-excluded messages in the thread, -rather than the number of matching messages. +appear in displayed results. ``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ **addrfold**\ \|\ **nameaddrfold**\) diff --git a/notmuch-search.c b/notmuch-search.c index 04e33c6..246ec0a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -252,7 +252,9 @@ do_search_threads (search_context_t *ctx) return 0; } -/* Returns TRUE iff name and/or addr is considered duplicate. */ +/* Returns TRUE iff name and/or addr is considered duplicate. If not, + * stores the name/addr pair in order to detect subsequent + * duplicates. */ static notmuch_bool_t is_duplicate (const search_context_t *ctx, const char *name, const char *addr) { @@ -405,8 +407,9 @@ process_address_header (const search_context_t *ctx, const char *value) g_object_unref (list); } +/* Destructor for talloc-allocated GHashTable keys and values. */ static void -_my_talloc_free_for_g_hash (void *ptr) +_talloc_free_for_g_hash (void *ptr) { talloc_free (ptr); } @@ -679,12 +682,6 @@ static const notmuch_opt_des
Re: [PATCH v2 06/10] cli: Introduce notmuch address command
On Wed, Nov 05 2014, Mark Walters wrote: On Tue, 04 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote: On Tue, Nov 04 2014, Mark Walters wrote: On Mon, 03 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote: This moves address-related functionality from search command to the new address command. The implementation shares almost all code and some command line options. Options --offset and --limit were intentionally not included in the address command, because they refer to messages numbers, which users do not see in the output. This could confuse users because, for example, they could see more addresses in the output that what was specified with --limit. This functionality can be correctly reimplemented for addresses later. I am not sure about this: we already have this anomaly for output=files say. Also I can imagine calling notmuch address --limit=1000 ... to get a bunch of recent addresses quickly and I really am wanting to look at 1000 messages, not collect 1000 addresses. I think that one of the reasons for having the new address command is to have cleaner user interface. And including anomalies doesn't sound like a way to achieve this. I think that now you can use date: query to limit the search. I volunteer to implement address --limit properly after 0.19. This should be easy. I think this depends on how you view limit: is it to limit the output (roughly to run head on the output), or is to bound the amount of work notmuch has to do (eg to make sure you don't get a long delay). Your suggestion is definitely the former, whereas I am more worried about the latter: limit in your definition could take an essentially unbounded amount of time. Why? If I understand you correctly, you think of limit in terms of messages. There is 1:N mapping between messages and addresses, where N = 1. If I limit the number of printed addresses, I limit the number of messages as well. Only if N is zero (which probably can be the case with Bcc and --output=recipients) then it can result in unbounded work (provided you have infinite number of Bcc only messages in your database :-)). Do I miss something? -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 06/10] cli: Introduce "notmuch address" command
On Tue, Nov 04 2014, Mark Walters wrote: > On Mon, 03 Nov 2014, Michal Sojka wrote: >> This moves address-related functionality from search command to the >> new address command. The implementation shares almost all code and >> some command line options. >> >> Options --offset and --limit were intentionally not included in the >> address command, because they refer to messages numbers, which users >> do not see in the output. This could confuse users because, for >> example, they could see more addresses in the output that what was >> specified with --limit. This functionality can be correctly >> reimplemented for addresses later. > > I am not sure about this: we already have this anomaly for output=files > say. Also I can imagine calling notmuch address --limit=1000 ... to get > a bunch of recent addresses quickly and I really am wanting to look at > 1000 messages, not collect 1000 addresses. I think that one of the reasons for having the new "address" command is to have cleaner user interface. And including "anomalies" doesn't sound like a way to achieve this. I think that now you can use "date:" query to limit the search. I volunteer to implement "address --limit" properly after 0.19. This should be easy. -Michal > Additionally, the 1000 message approach makes sense when we start > deduping whereas 1000 authors becomes unclear. > >> >> This was inspired by a patch from Jani Nikula. >> --- >> completion/notmuch-completion.bash | 42 ++- >> completion/notmuch-completion.zsh | 10 +++- >> doc/man1/notmuch-address.rst | 99 >> >> doc/man1/notmuch-search.rst| 20 +--- >> doc/man1/notmuch.rst | 7 +-- >> notmuch-client.h | 3 ++ >> notmuch-search.c | 101 >> + >> notmuch.c | 2 + >> 8 files changed, 228 insertions(+), 56 deletions(-) >> create mode 100644 doc/man1/notmuch-address.rst >> >> diff --git a/completion/notmuch-completion.bash >> b/completion/notmuch-completion.bash >> index cfbd389..94ea2d5 100644 >> --- a/completion/notmuch-completion.bash >> +++ b/completion/notmuch-completion.bash >> @@ -294,7 +294,7 @@ _notmuch_search() >> return >> ;; >> --output) >> -COMPREPLY=( $( compgen -W "summary threads messages files tags >> sender recipients" -- "${cur}" ) ) >> +COMPREPLY=( $( compgen -W "summary threads messages files tags" -- >> "${cur}" ) ) >> return >> ;; >> --sort) >> @@ -320,6 +320,44 @@ _notmuch_search() >> esac >> } >> >> +_notmuch_address() >> +{ >> +local cur prev words cword split >> +_init_completion -s || return >> + >> +$split && >> +case "${prev}" in >> +--format) >> +COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) ) >> +return >> +;; >> +--output) >> +COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) >> +return >> +;; >> +--sort) >> +COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) >> ) >> +return >> +;; >> +--exclude) >> +COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) ) >> +return >> +;; >> +esac >> + >> +! $split && >> +case "${cur}" in >> +-*) >> +local options="--format= --output= --sort= --exclude=" >> +compopt -o nospace >> +COMPREPLY=( $(compgen -W "$options" -- ${cur}) ) >> +;; >> +*) >> +_notmuch_search_terms >> +;; >> +esac >> +} >> + >> _notmuch_show() >> { >> local cur prev words cword split >> @@ -393,7 +431,7 @@ _notmuch_tag() >> >> _notmuch() >> { >> -local _notmuch_commands="compact config count dump help insert new >> reply restore search setup show tag" >> +local _notmuch_commands="compact config count dump help insert new >> reply restore search address setup show tag" >> local arg cur prev words cword split >> >> # require bash-completion with _init_completion >>
[PATCH v2 06/10] cli: Introduce "notmuch address" command
On Tue, Nov 04 2014, David Bremner wrote: > Michal Sojka writes: > >> +{ >> +local cur prev words cword split >> +_init_completion -s || return >> + >> +$split && >> +case "${prev}" in >> +--format) >> +COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) ) >> +return >> +;; >> +--output) >> +COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) >> +return >> +;; >> +--sort) >> +COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) >> ) >> +return >> +;; >> +--exclude) >> +COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) ) >> +return >> +;; >> +esac >> + >> +! $split && >> +case "${cur}" in >> +-*) >> +local options="--format= --output= --sort= --exclude=" >> +compopt -o nospace >> +COMPREPLY=( $(compgen -W "$options" -- ${cur}) ) >> +;; >> +*) >> +_notmuch_search_terms >> +;; >> +esac >> +} >> + > > I am reminded that we have no tests for shell completion stuff, which > seems pretty fragile. > >> + >> +``--format=``\ (**json**\ \|\ **sexp**\ \|\ **text**\ \|\ **text0**) >> +Presents the results in either JSON, S-Expressions, newline >> +character separated plain-text (default), or null character >> +separated plain-text (compatible with **xargs(1)** -0 option >> +where available). >> + >> +``--format-version=N`` >> +Use the specified structured output format version. This is >> +intended for programs that invoke **notmuch(1)** internally. If >> +omitted, the latest supported version will be used. >> + > > > I wonder if at some point we should have a notmuch-output-formats.7 page. > > >> +``--exclude=(true|false|all|flag)`` >> +A message is called "excluded" if it matches at least one tag in >> +search.tag\_exclude that does not appear explicitly in the >> +search terms. This option specifies whether to omit excluded >> +messages in the search process. > > Similarly for excludes. I'm ok with the duplication for now, and I can > see an argument for not making the user chase references. What about using RST include directive [1] to include shared parts into more documents? [1] http://docutils.sourceforge.net/docs/ref/rst/directives.html#including-an-external-document-fragment -Michal
[PATCH v2 08/10] cli: address: Do not output duplicate addresses
On Tue, Nov 04 2014, David Bremner wrote: > Michal Sojka writes: > >> >> +/* Returns TRUE iff name and addr is duplicate. */ > > If you're revising this patch, it would be good to mention the side > effect of this function. > >> -process_address_list (const search_context_t *ctx, InternetAddressList >> *list) >> +process_address_list (const search_context_t *ctx, >> + InternetAddressList *list) > > It probably doesn't make any difference, but this looks like a needless > whitespace change. > > This function definitely needs some comment / pointer to > documention. And probably not to have _my in the name. > >> +static void >> +_my_talloc_free_for_g_hash (void *ptr) >> +{ >> +talloc_free (ptr); >> +} >> + > > I don't understand the name of the next subtest > >> +test_begin_subtest "No --output" >> +notmuch address --output=sender --output=recipients '*' >OUTPUT This should be "notmuch address '*' >OUTPUT". I'll fix that. >> +# Use EXPECTED from previous subtest >> +test_expect_equal_file OUTPUT EXPECTED >> + >> + >> +test_done > > nitpick, extra blank lines > > So, AIUI, this is all of the series proposed for 0.19. Agreed. > It looks close to OK to me, modulo some minor style nits. One > anonymous commentator on IRC mentioned the use of module scope > variables, I guess in patch 6/10. I'm not sure of a better solution, > but it's true in a perfect world we wouldn't have module local state. A possible solution would be fill in common_options structure programmatically, but this would make the code much less readable. I can think of a few other solutions but none of them would fit into "perfect world" :) I'll send updated patches in the evening (CET timezone). Thanks -Michal
[PATCH v2 07/10] cli: search: Convert --output to keyword argument
On Tue, Nov 04 2014, Mark Walters wrote: > Hi > > On Mon, 03 Nov 2014, Michal Sojka wrote: >> Now, when address related outputs are in a separate command, it makes >> no sense to combine multiple --output options in search command line. >> Using switch statement to handle different outputs is more readable >> than a series of if statements. > > I am not keen on this change: I think the user should always be able to > force the default output by setting command line options (which should > protect against future changes in the default). You can do this even with this patch. > Thus I would like to continue to allow --output=sender > --output=recipients. This is allowed in notmuch address. This patch modifies only notmuch search. Currently it makes no sense to run --output=files --output=tags or other output combinations. > I do approve of making the default do something useful but whether it > should be both or just sender (which is much faster) is unclear to me. Does this comment refer to 7/10 or to 6/10? I'd say it refers to the following line from 6/10. search_context.output = OUTPUT_SENDER | OUTPUT_RECIPIENTS; Even there you can override the default by command line option. This get executed only if no --output appears on the command line. Thanks -Michal > > Best wishes > > Mark > > >> --- >> doc/man1/notmuch-search.rst | 3 --- >> notmuch-search.c| 25 + >> 2 files changed, 13 insertions(+), 15 deletions(-) >> >> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst >> index 65df288..0cc2911 100644 >> --- a/doc/man1/notmuch-search.rst >> +++ b/doc/man1/notmuch-search.rst >> @@ -78,9 +78,6 @@ Supported options for **search** include >> by null characters (--format=text0), as a JSON array >> (--format=json), or as an S-Expression list (--format=sexp). >> >> -This option can be given multiple times to combine different >> -outputs. >> - >> ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) >> This option can be used to present results in either >> chronological order (**oldest-first**) or reverse chronological >> diff --git a/notmuch-search.c b/notmuch-search.c >> index cbd84f5..402e860 100644 >> --- a/notmuch-search.c >> +++ b/notmuch-search.c >> @@ -593,7 +593,7 @@ notmuch_search_command (notmuch_config_t *config, int >> argc, char *argv[]) >> int opt_index, ret; >> >> notmuch_opt_desc_t options[] = { >> -{ NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o', >> +{ NOTMUCH_OPT_KEYWORD, >output, "output", 'o', >>(notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, >>{ "threads", OUTPUT_THREADS }, >>{ "messages", OUTPUT_MESSAGES }, >> @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int >> argc, char *argv[]) >> { 0, 0, 0, 0, 0 } >> }; >> >> +ctx->output = OUTPUT_SUMMARY; >> opt_index = parse_arguments (argc, argv, options, 1); >> if (opt_index < 0) >> return EXIT_FAILURE; >> >> -if (! ctx->output) >> -ctx->output = OUTPUT_SUMMARY; >> - >> if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES && >> ctx->dupe != -1) { >> fprintf (stderr, "Error: --duplicate=N is only supported with >> --output=files and --output=messages.\n"); >> @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int >> argc, char *argv[]) >> argc - opt_index, argv + opt_index)) >> return EXIT_FAILURE; >> >> -if (ctx->output == OUTPUT_SUMMARY || >> -ctx->output == OUTPUT_THREADS) >> +switch (ctx->output) { >> +case OUTPUT_SUMMARY: >> +case OUTPUT_THREADS: >> ret = do_search_threads (ctx); >> -else if (ctx->output == OUTPUT_MESSAGES || >> - ctx->output == OUTPUT_FILES) >> +break; >> +case OUTPUT_MESSAGES: >> +case OUTPUT_FILES: >> ret = do_search_messages (ctx); >> -else if (ctx->output == OUTPUT_TAGS) >> +break; >> +case OUTPUT_TAGS: >> ret = do_search_tags (ctx); >> -else { >> -fprintf (stderr, "Error: the combination of outputs is not >> supported.\n"); >> -ret = 1; >> +break; >> +default: >> +INTERNAL_ERROR ("Unexpected output"); >> } >> >> _notmuch_search_cleanup (ctx); >> -- >> 2.1.1 >> >> ___ >> notmuch mailing list >> notmuch at notmuchmail.org >> http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 10/10] cli: address: Add --filter-by option to configure address filtering
This option allows to configure the criterion for duplicate address filtering. Without this option, all unique combinations of name and address parts are printed. This option allows to filter the output more, for example to only contain unique address parts. --- completion/notmuch-completion.bash | 6 +++- completion/notmuch-completion.zsh | 1 + doc/man1/notmuch-address.rst | 36 ++- notmuch-search.c | 48 +++-- test/T097-address-filter-by.sh | 73 ++ 5 files changed, 160 insertions(+), 4 deletions(-) create mode 100755 test/T097-address-filter-by.sh diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index db152f3..2cb1586 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -310,7 +310,7 @@ _notmuch_search() ! $split && case "${cur}" in -*) - local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate=" + local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate= --filter-by=" compopt -o nospace COMPREPLY=( $(compgen -W "$options" -- ${cur}) ) ;; @@ -343,6 +343,10 @@ _notmuch_address() COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) ) return ;; + --filter-by) + COMPREPLY=( $( compgen -W "nameaddr name addr addrfold nameaddrfold" -- "${cur}" ) ) + return + ;; esac ! $split && diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 8968562..3758f1a 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -62,6 +62,7 @@ _notmuch_address() _arguments -s : \ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ '--output=[select what to output]:output:((sender recipients count))' +'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both name and address part" name\:"name part" addr\:"address part" addrfold\:"case-insensitive address part" nameaddrfold\:"name and case-insensitive address part"))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 18473a7..524ab91 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -11,7 +11,8 @@ DESCRIPTION === Search for messages matching the given search terms, and display the -addresses from them. Duplicate addresses are filtered out. +addresses from them. Duplicate addresses are filtered out. Filtering +can be configured with the --filter-by option. See **notmuch-search-terms(7)** for details of the supported syntax for . @@ -85,6 +86,39 @@ Supported options for **address** include is the number of matching non-excluded messages in the thread, rather than the number of matching messages. +``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ **addrfold**\ \|\ **nameaddrfold**\) + + Controls how to filter out duplicate addresses. The filtering + algorithm receives a sequence of email addresses and outputs + the same sequence without the addresses that are considered a + duplicate of a previously output address. What is considered a + duplicate depends on how the two addresses are compared: + + **nameaddr** means that both name and address parts are + compared in case-sensitive manner. Therefore, all same looking + addresses strings are considered duplicate. This is the + default. + + **name** means that only the name part is compared (in + case-sensitive manner). For example, the addresses "John Doe + " and "John Doe " will be + considered duplicate. + + **addr** means that only the address part is compared (in + case-sensitive manner). For example, the addresses "John Doe + " and "Dr. John Doe " will + be considered duplicate. + + **addrfold** is like **addr**, but comparison is done in + canse-insensitive manner. For example, the addresses "John Doe + " and "Dr. John Doe " will + be considered duplicate. + + **nameaddrfold** is like **nameaddr**, but address comparison + is done in canse-insensitive manner. For example, the + addresses "John Doe " and "John Doe + " will be considered duplicate. + EXIT STATUS === diff --git a/notmuch-search.c b/notmuch-search.c index d99e530..04e33c6 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -43,6 +43,14 @@ typedef enum { NOTMUCH_FORMAT_SEXP } format_sel_t; +typedef enum { +FILTER_BY_NAMEADDR = 0, +FILTER_BY_NAME, +FILTER_BY_ADDR, +FILTER_BY_ADDRFOLD, +FILTER_BY_NAMEADDRFOLD, +} filter_by_t; + typedef struct { notmuch_database_t *notmuch;
[PATCH v2 09/10] cli: address: Add --output=count
This output prints how many times was each address encountered during search. --- completion/notmuch-completion.bash | 2 +- completion/notmuch-completion.zsh | 2 +- doc/man1/notmuch-address.rst | 7 ++ notmuch-search.c | 49 -- test/T095-address.sh | 48 + 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 94ea2d5..db152f3 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -332,7 +332,7 @@ _notmuch_address() return ;; --output) - COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) + COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) ) return ;; --sort) diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index c606b75..8968562 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -61,7 +61,7 @@ _notmuch_address() { _arguments -s : \ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ -'--output=[select what to output]:output:((sender recipients))' +'--output=[select what to output]:output:((sender recipients count))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 96512b7..18473a7 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -48,6 +48,13 @@ Supported options for **address** include Output all addresses from the *To*, *Cc* and *Bcc* headers. + **count** + Print the count of how many times was the address + encountered during search. + + Note: With this option, addresses are printed only after + the whole search is finished. This may take long time. + ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological diff --git a/notmuch-search.c b/notmuch-search.c index 741702a..d99e530 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -33,6 +33,7 @@ typedef enum { /* Address command */ OUTPUT_SENDER = 1 << 5, OUTPUT_RECIPIENTS = 1 << 6, +OUTPUT_COUNT = 1 << 7, } output_t; typedef enum { @@ -59,6 +60,7 @@ typedef struct { typedef struct { const char *name; const char *addr; +int count; } mailbox_t; /* Return two stable query strings that identify exactly the matched @@ -247,17 +249,24 @@ is_duplicate (const search_context_t *ctx, const char *name, const char *addr) { notmuch_bool_t duplicate; char *key; +mailbox_t *mailbox; key = talloc_asprintf (ctx->format, "%s <%s>", name, addr); if (! key) return FALSE; -duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL); +duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, (gpointer)); -if (! duplicate) - g_hash_table_insert (ctx->addresses, key, NULL); -else +if (! duplicate) { + mailbox = talloc (ctx->format, mailbox_t); + mailbox->name = talloc_strdup (mailbox, name); + mailbox->addr = talloc_strdup (mailbox, addr); + mailbox->count = 1; + g_hash_table_insert (ctx->addresses, key, mailbox); +} else { + mailbox->count++; talloc_free (key); +} return duplicate; } @@ -267,6 +276,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { const char *name = mailbox->name; const char *addr = mailbox->addr; +int count = mailbox->count; sprinter_t *format = ctx->format; InternetAddress *ia = internet_address_mailbox_new (name, addr); char *name_addr; @@ -276,6 +286,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) name_addr = internet_address_to_string (ia, FALSE); if (format->is_text_printer) { + if (count > 0) { + format->integer (format, count); + format->string (format, "\t"); + } format->string (format, name_addr); format->separator (format); } else { @@ -286,6 +300,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) format->string (format, addr); format->map_key (format, "name-addr"); format->string (format, name_addr); + if (count > 0) { + format->map_key (format, "count"); + format->integer (format, count); + } format->end (format); format->separator (format); } @@ -294,7 +312,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) g_free (name_addr); } -/* Print addresses from InternetAddressList. */
[PATCH v2 08/10] cli: address: Do not output duplicate addresses
This filters out duplicate addresses from address command output. It also also adds tests for the address command. The code here is an extended version of a patch from Jani Nikula. --- doc/man1/notmuch-address.rst | 2 +- notmuch-search.c | 40 - test/T095-address.sh | 100 +++ 3 files changed, 140 insertions(+), 2 deletions(-) create mode 100755 test/T095-address.sh diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 8109f11..96512b7 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -11,7 +11,7 @@ DESCRIPTION === Search for messages matching the given search terms, and display the -addresses from them. +addresses from them. Duplicate addresses are filtered out. See **notmuch-search-terms(7)** for details of the supported syntax for . diff --git a/notmuch-search.c b/notmuch-search.c index 402e860..741702a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -53,6 +53,7 @@ typedef struct { int offset; int limit; int dupe; +GHashTable *addresses; } search_context_t; typedef struct { @@ -240,6 +241,27 @@ do_search_threads (search_context_t *ctx) return 0; } +/* Returns TRUE iff name and addr is duplicate. */ +static notmuch_bool_t +is_duplicate (const search_context_t *ctx, const char *name, const char *addr) +{ +notmuch_bool_t duplicate; +char *key; + +key = talloc_asprintf (ctx->format, "%s <%s>", name, addr); +if (! key) + return FALSE; + +duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL); + +if (! duplicate) + g_hash_table_insert (ctx->addresses, key, NULL); +else + talloc_free (key); + +return duplicate; +} + static void print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { @@ -274,7 +296,8 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) /* Print addresses from InternetAddressList. */ static void -process_address_list (const search_context_t *ctx, InternetAddressList *list) +process_address_list (const search_context_t *ctx, + InternetAddressList *list) { InternetAddress *address; int i; @@ -298,6 +321,9 @@ process_address_list (const search_context_t *ctx, InternetAddressList *list) .addr = internet_address_mailbox_get_addr (mailbox), }; + if (is_duplicate (ctx, mbx.name, mbx.addr)) + continue; + print_mailbox (ctx, ); } } @@ -321,6 +347,12 @@ process_address_header (const search_context_t *ctx, const char *value) g_object_unref (list); } +static void +_my_talloc_free_for_g_hash (void *ptr) +{ +talloc_free (ptr); +} + static int _count_filenames (notmuch_message_t *message) { @@ -669,8 +701,14 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; +ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal, + _my_talloc_free_for_g_hash, NULL); + ret = do_search_messages (ctx); +g_hash_table_unref (ctx->addresses); + + _notmuch_search_cleanup (ctx); return ret ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/test/T095-address.sh b/test/T095-address.sh new file mode 100755 index 000..8a256d2 --- /dev/null +++ b/test/T095-address.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +test_description='"notmuch address" in several variants' +. ./test-lib.sh + +add_email_corpus + +test_begin_subtest "--output=sender" +notmuch address --output=sender '*' >OUTPUT +catOUTPUT +cat
[PATCH v2 07/10] cli: search: Convert --output to keyword argument
Now, when address related outputs are in a separate command, it makes no sense to combine multiple --output options in search command line. Using switch statement to handle different outputs is more readable than a series of if statements. --- doc/man1/notmuch-search.rst | 3 --- notmuch-search.c| 25 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst index 65df288..0cc2911 100644 --- a/doc/man1/notmuch-search.rst +++ b/doc/man1/notmuch-search.rst @@ -78,9 +78,6 @@ Supported options for **search** include by null characters (--format=text0), as a JSON array (--format=json), or as an S-Expression list (--format=sexp). - This option can be given multiple times to combine different - outputs. - ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological diff --git a/notmuch-search.c b/notmuch-search.c index cbd84f5..402e860 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -593,7 +593,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) int opt_index, ret; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o', + { NOTMUCH_OPT_KEYWORD, >output, "output", 'o', (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, { "threads", OUTPUT_THREADS }, { "messages", OUTPUT_MESSAGES }, @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0, 0, 0, 0 } }; +ctx->output = OUTPUT_SUMMARY; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) return EXIT_FAILURE; -if (! ctx->output) - ctx->output = OUTPUT_SUMMARY; - if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES && ctx->dupe != -1) { fprintf (stderr, "Error: --duplicate=N is only supported with --output=files and --output=messages.\n"); @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -if (ctx->output == OUTPUT_SUMMARY || - ctx->output == OUTPUT_THREADS) +switch (ctx->output) { +case OUTPUT_SUMMARY: +case OUTPUT_THREADS: ret = do_search_threads (ctx); -else if (ctx->output == OUTPUT_MESSAGES || -ctx->output == OUTPUT_FILES) + break; +case OUTPUT_MESSAGES: +case OUTPUT_FILES: ret = do_search_messages (ctx); -else if (ctx->output == OUTPUT_TAGS) + break; +case OUTPUT_TAGS: ret = do_search_tags (ctx); -else { - fprintf (stderr, "Error: the combination of outputs is not supported.\n"); - ret = 1; + break; +default: + INTERNAL_ERROR ("Unexpected output"); } _notmuch_search_cleanup (ctx); -- 2.1.1
[PATCH v2 06/10] cli: Introduce "notmuch address" command
This moves address-related functionality from search command to the new address command. The implementation shares almost all code and some command line options. Options --offset and --limit were intentionally not included in the address command, because they refer to messages numbers, which users do not see in the output. This could confuse users because, for example, they could see more addresses in the output that what was specified with --limit. This functionality can be correctly reimplemented for addresses later. This was inspired by a patch from Jani Nikula. --- completion/notmuch-completion.bash | 42 ++- completion/notmuch-completion.zsh | 10 +++- doc/man1/notmuch-address.rst | 99 doc/man1/notmuch-search.rst| 20 +--- doc/man1/notmuch.rst | 7 +-- notmuch-client.h | 3 ++ notmuch-search.c | 101 + notmuch.c | 2 + 8 files changed, 228 insertions(+), 56 deletions(-) create mode 100644 doc/man1/notmuch-address.rst diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cfbd389..94ea2d5 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) - COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) ) + COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) ) return ;; --sort) @@ -320,6 +320,44 @@ _notmuch_search() esac } +_notmuch_address() +{ +local cur prev words cword split +_init_completion -s || return + +$split && +case "${prev}" in + --format) + COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) ) + return + ;; + --output) + COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) + return + ;; + --sort) + COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) ) + return + ;; + --exclude) + COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) ) + return + ;; +esac + +! $split && +case "${cur}" in + -*) + local options="--format= --output= --sort= --exclude=" + compopt -o nospace + COMPREPLY=( $(compgen -W "$options" -- ${cur}) ) + ;; + *) + _notmuch_search_terms + ;; +esac +} + _notmuch_show() { local cur prev words cword split @@ -393,7 +431,7 @@ _notmuch_tag() _notmuch() { -local _notmuch_commands="compact config count dump help insert new reply restore search setup show tag" +local _notmuch_commands="compact config count dump help insert new reply restore search address setup show tag" local arg cur prev words cword split # require bash-completion with _init_completion diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 3e52a00..c606b75 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -10,6 +10,7 @@ _notmuch_commands() 'setup:interactively set up notmuch for first use' 'new:find and import any new message to the database' 'search:search for messages matching the search terms, display matching threads as results' +'address:get addresses from messages matching the given search terms' 'reply:constructs a reply template for a set of messages' 'show:show all messages matching the search terms' 'tag:add or remove tags for all messages matching the search terms' @@ -53,7 +54,14 @@ _notmuch_search() '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ -'--output=[select what to output]:output:((summary threads messages files tags sender recipients))' +'--output=[select what to output]:output:((summary threads messages files tags))' +} + +_notmuch_address() +{ + _arguments -s : \ +'--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \ +'--output=[select what to output]:output:((sender recipients))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst new file mode 100644 index 000..8109f11 --- /dev/null +++ b/doc/man1/notmuch-address.rst @@ -0,0 +1,99 @@ +=== +notmuch-address +=== + +SYNOPSIS + + +**notmuch** **address** [*option* ...] <*search-term*> ... + +DESCRIPTION
[PATCH v2 05/10] cli: add support for hierarchical command line option arrays
From: Jani NikulaNOTMUCH_OPT_INHERIT expects a notmuch_opt_desc_t * pointer in output_var. The "Unrecognized option" message was moved out of parse_option() to not be emitted twice or when parsing a non-inherited option. --- command-line-arguments.c | 16 +--- command-line-arguments.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/command-line-arguments.c b/command-line-arguments.c index c6f7269..de6b453 100644 --- a/command-line-arguments.c +++ b/command-line-arguments.c @@ -122,16 +122,18 @@ parse_position_arg (const char *arg_str, int pos_arg_index, */ notmuch_bool_t -parse_option (const char *arg, - const notmuch_opt_desc_t *options) { - -assert(arg); +parse_option (const char *_arg, const notmuch_opt_desc_t *options) +{ +assert(_arg); assert(options); -arg += 2; - +const char *arg = _arg + 2; /* _arg starts with -- */ const notmuch_opt_desc_t *try; for (try = options; try->opt_type != NOTMUCH_OPT_END; try++) { + if (try->opt_type == NOTMUCH_OPT_INHERIT && + parse_option (_arg, try->output_var)) + return TRUE; + if (! try->name) continue; @@ -170,7 +172,6 @@ parse_option (const char *arg, /*UNREACHED*/ } } -fprintf (stderr, "Unrecognized option: --%s\n", arg); return FALSE; } @@ -201,6 +202,7 @@ parse_arguments (int argc, char **argv, if (more_args) { opt_index++; } else { + fprintf (stderr, "Unrecognized option: %s\n", argv[opt_index]); opt_index = -1; } diff --git a/command-line-arguments.h b/command-line-arguments.h index 6444129..309aaf2 100644 --- a/command-line-arguments.h +++ b/command-line-arguments.h @@ -5,6 +5,7 @@ enum notmuch_opt_type { NOTMUCH_OPT_END = 0, +NOTMUCH_OPT_INHERIT, /* another options table */ NOTMUCH_OPT_BOOLEAN, /* --verbose */ NOTMUCH_OPT_INT, /* --frob=8 */ NOTMUCH_OPT_KEYWORD, /* --format=raw|json|text */ -- 2.1.1
[PATCH v2 04/10] cli: search: Split notmuch_search_command to smaller functions
In the next commit, these functions will be used to share some functionality between search and address commands. --- notmuch-search.c | 155 ++- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6765a16..f115359 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx) return 0; } +static int +_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int argc, char *argv[]) +{ +char *query_str; +unsigned int i; + +switch (ctx->format_sel) { +case NOTMUCH_FORMAT_TEXT: + ctx->format = sprinter_text_create (config, stdout); + break; +case NOTMUCH_FORMAT_TEXT0: + if (ctx->output == OUTPUT_SUMMARY) { + fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); + return EXIT_FAILURE; + } + ctx->format = sprinter_text0_create (config, stdout); + break; +case NOTMUCH_FORMAT_JSON: + ctx->format = sprinter_json_create (config, stdout); + break; +case NOTMUCH_FORMAT_SEXP: + ctx->format = sprinter_sexp_create (config, stdout); + break; +default: + /* this should never happen */ + INTERNAL_ERROR("no output format selected"); +} + +notmuch_exit_if_unsupported_format (); + +if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch)) + return EXIT_FAILURE; + +query_str = query_string_from_args (ctx->notmuch, argc, argv); +if (query_str == NULL) { + fprintf (stderr, "Out of memory.\n"); + return EXIT_FAILURE; +} +if (*query_str == '\0') { + fprintf (stderr, "Error: notmuch search requires at least one search term.\n"); + return EXIT_FAILURE; +} + +ctx->query = notmuch_query_create (ctx->notmuch, query_str); +if (ctx->query == NULL) { + fprintf (stderr, "Out of memory\n"); + return EXIT_FAILURE; +} + +notmuch_query_set_sort (ctx->query, ctx->sort); + +if (ctx->exclude == NOTMUCH_EXCLUDE_FLAG && ctx->output != OUTPUT_SUMMARY) { + /* If we are not doing summary output there is nowhere to +* print the excluded flag so fall back on including the +* excluded messages. */ + fprintf (stderr, "Warning: this output format cannot flag excluded messages.\n"); + ctx->exclude = NOTMUCH_EXCLUDE_FALSE; +} + +if (ctx->exclude != NOTMUCH_EXCLUDE_FALSE) { + const char **search_exclude_tags; + size_t search_exclude_tags_length; + + search_exclude_tags = notmuch_config_get_search_exclude_tags + (config, _exclude_tags_length); + for (i = 0; i < search_exclude_tags_length; i++) + notmuch_query_add_tag_exclude (ctx->query, search_exclude_tags[i]); + notmuch_query_set_omit_excluded (ctx->query, ctx->exclude); +} + +return 0; +} + +static void +_notmuch_search_cleanup (search_context_t *ctx) +{ +notmuch_query_destroy (ctx->query); +notmuch_database_destroy (ctx->notmuch); + +talloc_free (ctx->format); +} + int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { @@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .dupe = -1, }; search_context_t *ctx = _context; -char *query_str; int opt_index, ret; -unsigned int i; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, >sort, "sort", 's', @@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (ctx->format_sel) { -case NOTMUCH_FORMAT_TEXT: - ctx->format = sprinter_text_create (config, stdout); - break; -case NOTMUCH_FORMAT_TEXT0: - if (ctx->output == OUTPUT_SUMMARY) { - fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); - return EXIT_FAILURE; - } - ctx->format = sprinter_text0_create (config, stdout); - break; -case NOTMUCH_FORMAT_JSON: - ctx->format = sprinter_json_create (config, stdout); - break; -case NOTMUCH_FORMAT_SEXP: - ctx->format = sprinter_sexp_create (config, stdout); - break; -default: - /* this should never happen */ - INTERNAL_ERROR("no output format selected"); -} - -notmuch_exit_if_unsupported_format (); - -if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch)) +if (_notmuch_search_prepare (ctx, config, +argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -query_str = query_string_from_args (ctx->notmuch, argc-opt_index, argv+opt_index); -if
[PATCH v2 03/10] cli: search: Convert ctx. to ctx->
Mostly text replacement. --- notmuch-search.c | 81 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 3d2012b..6765a16 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -474,7 +474,7 @@ do_search_tags (const search_context_t *ctx) int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -search_context_t ctx = { +search_context_t search_context = { .format_sel = NOTMUCH_FORMAT_TEXT, .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, @@ -483,23 +483,24 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .limit = -1, /* unlimited */ .dupe = -1, }; +search_context_t *ctx = _context; char *query_str; int opt_index, ret; unsigned int i; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD, , "sort", 's', + { NOTMUCH_OPT_KEYWORD, >sort, "sort", 's', (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST }, { "newest-first", NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f', + { NOTMUCH_OPT_KEYWORD, >format_sel, "format", 'f', (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, { "sexp", NOTMUCH_FORMAT_SEXP }, { "text", NOTMUCH_FORMAT_TEXT }, { "text0", NOTMUCH_FORMAT_TEXT0 }, { 0, 0 } } }, { NOTMUCH_OPT_INT, _format_version, "format-version", 0, 0 }, - { NOTMUCH_OPT_KEYWORD_FLAGS, , "output", 'o', + { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o', (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, { "threads", OUTPUT_THREADS }, { "messages", OUTPUT_MESSAGES }, @@ -508,15 +509,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { "files", OUTPUT_FILES }, { "tags", OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x', +{ NOTMUCH_OPT_KEYWORD, >exclude, "exclude", 'x', (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE }, { "false", NOTMUCH_EXCLUDE_FALSE }, { "flag", NOTMUCH_EXCLUDE_FLAG }, { "all", NOTMUCH_EXCLUDE_ALL }, { 0, 0 } } }, - { NOTMUCH_OPT_INT, , "offset", 'O', 0 }, - { NOTMUCH_OPT_INT, , "limit", 'L', 0 }, - { NOTMUCH_OPT_INT, , "duplicate", 'D', 0 }, + { NOTMUCH_OPT_INT, >offset, "offset", 'O', 0 }, + { NOTMUCH_OPT_INT, >limit, "limit", 'L', 0 }, + { NOTMUCH_OPT_INT, >dupe, "duplicate", 'D', 0 }, { 0, 0, 0, 0, 0 } }; @@ -524,31 +525,31 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; -if (! ctx.output) - ctx.output = OUTPUT_SUMMARY; +if (! ctx->output) + ctx->output = OUTPUT_SUMMARY; -if (ctx.output != OUTPUT_FILES && ctx.output != OUTPUT_MESSAGES && - ctx.dupe != -1) { +if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES && + ctx->dupe != -1) { fprintf (stderr, "Error: --duplicate=N is only supported with --output=files and --output=messages.\n"); return EXIT_FAILURE; } -switch (ctx.format_sel) { +switch (ctx->format_sel) { case NOTMUCH_FORMAT_TEXT: - ctx.format = sprinter_text_create (config, stdout); + ctx->format = sprinter_text_create (config, stdout); break; case NOTMUCH_FORMAT_TEXT0: - if (ctx.output == OUTPUT_SUMMARY) { + if (ctx->output == OUTPUT_SUMMARY) { fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); return EXIT_FAILURE; } - ctx.format = sprinter_text0_create (config, stdout); + ctx->format = sprinter_text0_create (config, stdout); break; case NOTMUCH_FORMAT_JSON: - ctx.format = sprinter_json_create (config, stdout); + ctx->format = sprinter_json_create (config, stdout); break; case NOTMUCH_FORMAT_SEXP: - ctx.format = sprinter_sexp_create (config, stdout); + ctx->format = sprinter_sexp_create (config, stdout); break; default: /* this should never happen */ @@ -558,10 +559,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_exit_if_unsupported_format (); if (notmuch_database_open (notmuch_config_get_database_path (config), -
[PATCH v2 02/10] cli: search: Move more variables into search_context_t
Just refactoring, no functional changes. --- notmuch-search.c | 49 ++--- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 2c47b80..3d2012b 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -34,8 +34,18 @@ typedef enum { #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS) +typedef enum { +NOTMUCH_FORMAT_JSON, +NOTMUCH_FORMAT_TEXT, +NOTMUCH_FORMAT_TEXT0, +NOTMUCH_FORMAT_SEXP +} format_sel_t; + typedef struct { +notmuch_database_t *notmuch; +format_sel_t format_sel; sprinter_t *format; +notmuch_exclude_t exclude; notmuch_query_t *query; notmuch_sort_t sort; output_t output; @@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx) } static int -do_search_tags (notmuch_database_t *notmuch, - const search_context_t *ctx) +do_search_tags (const search_context_t *ctx) { notmuch_messages_t *messages = NULL; notmuch_tags_t *tags; const char *tag; sprinter_t *format = ctx->format; notmuch_query_t *query = ctx->query; +notmuch_database_t *notmuch = ctx->notmuch; /* should the following only special case if no excluded terms * specified? */ @@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch, int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -notmuch_database_t *notmuch; search_context_t ctx = { + .format_sel = NOTMUCH_FORMAT_TEXT, + .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, .output = 0, .offset = 0, @@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) }; char *query_str; int opt_index, ret; -notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE; unsigned int i; -enum { - NOTMUCH_FORMAT_JSON, - NOTMUCH_FORMAT_TEXT, - NOTMUCH_FORMAT_TEXT0, - NOTMUCH_FORMAT_SEXP -} format_sel = NOTMUCH_FORMAT_TEXT; - notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, , "sort", 's', (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST }, { "newest-first", NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f', + { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f', (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, { "sexp", NOTMUCH_FORMAT_SEXP }, { "text", NOTMUCH_FORMAT_TEXT }, @@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { "files", OUTPUT_FILES }, { "tags", OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x', +{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x', (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE }, { "false", NOTMUCH_EXCLUDE_FALSE }, { "flag", NOTMUCH_EXCLUDE_FLAG }, @@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (format_sel) { +switch (ctx.format_sel) { case NOTMUCH_FORMAT_TEXT: ctx.format = sprinter_text_create (config, stdout); break; @@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_exit_if_unsupported_format (); if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, )) + NOTMUCH_DATABASE_MODE_READ_ONLY, )) return EXIT_FAILURE; -query_str = query_string_from_args (notmuch, argc-opt_index, argv+opt_index); +query_str = query_string_from_args (ctx.notmuch, argc-opt_index, argv+opt_index); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return EXIT_FAILURE; @@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -ctx.query = notmuch_query_create (notmuch, query_str); +ctx.query = notmuch_query_create (ctx.notmuch, query_str); if (ctx.query == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; @@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_query_set_sort (ctx.query, ctx.sort); -if (exclude == NOTMUCH_EXCLUDE_FLAG && ctx.output != OUTPUT_SUMMARY) { +if (ctx.exclude == NOTMUCH_EXCLUDE_FLAG && ctx.output != OUTPUT_SUMMARY) { /* If we are not doing summary output there is nowhere to * print the excluded flag so fall back on including the
[PATCH v2 01/10] cli: search: Rename options to context
Just text replacement, no other changes. --- notmuch-search.c | 142 +++ 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6345fb6..2c47b80 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -42,7 +42,7 @@ typedef struct { int offset; int limit; int dupe; -} search_options_t; +} search_context_t; typedef struct { const char *name; @@ -89,39 +89,39 @@ get_thread_query (notmuch_thread_t *thread, } static int -do_search_threads (search_options_t *opt) +do_search_threads (search_context_t *ctx) { notmuch_thread_t *thread; notmuch_threads_t *threads; notmuch_tags_t *tags; -sprinter_t *format = opt->format; +sprinter_t *format = ctx->format; time_t date; int i; -if (opt->offset < 0) { - opt->offset += notmuch_query_count_threads (opt->query); - if (opt->offset < 0) - opt->offset = 0; +if (ctx->offset < 0) { + ctx->offset += notmuch_query_count_threads (ctx->query); + if (ctx->offset < 0) + ctx->offset = 0; } -threads = notmuch_query_search_threads (opt->query); +threads = notmuch_query_search_threads (ctx->query); if (threads == NULL) return 1; format->begin_list (format); for (i = 0; -notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset + opt->limit); +notmuch_threads_valid (threads) && (ctx->limit < 0 || i < ctx->offset + ctx->limit); notmuch_threads_move_to_next (threads), i++) { thread = notmuch_threads_get (threads); - if (i < opt->offset) { + if (i < ctx->offset) { notmuch_thread_destroy (thread); continue; } - if (opt->output == OUTPUT_THREADS) { + if (ctx->output == OUTPUT_THREADS) { format->set_prefix (format, "thread"); format->string (format, notmuch_thread_get_thread_id (thread)); @@ -138,7 +138,7 @@ do_search_threads (search_options_t *opt) format->begin_map (format); - if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST) + if (ctx->sort == NOTMUCH_SORT_OLDEST_FIRST) date = notmuch_thread_get_oldest_date (thread); else date = notmuch_thread_get_newest_date (thread); @@ -230,11 +230,11 @@ do_search_threads (search_options_t *opt) } static void -print_mailbox (const search_options_t *opt, const mailbox_t *mailbox) +print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { const char *name = mailbox->name; const char *addr = mailbox->addr; -sprinter_t *format = opt->format; +sprinter_t *format = ctx->format; InternetAddress *ia = internet_address_mailbox_new (name, addr); char *name_addr; @@ -263,7 +263,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t *mailbox) /* Print addresses from InternetAddressList. */ static void -process_address_list (const search_options_t *opt, InternetAddressList *list) +process_address_list (const search_context_t *ctx, InternetAddressList *list) { InternetAddress *address; int i; @@ -279,7 +279,7 @@ process_address_list (const search_options_t *opt, InternetAddressList *list) if (group_list == NULL) continue; - process_address_list (opt, group_list); + process_address_list (ctx, group_list); } else { InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address); mailbox_t mbx = { @@ -287,14 +287,14 @@ process_address_list (const search_options_t *opt, InternetAddressList *list) .addr = internet_address_mailbox_get_addr (mailbox), }; - print_mailbox (opt, ); + print_mailbox (ctx, ); } } } /* Print addresses from a message header. */ static void -process_address_header (const search_options_t *opt, const char *value) +process_address_header (const search_context_t *ctx, const char *value) { InternetAddressList *list; @@ -305,7 +305,7 @@ process_address_header (const search_options_t *opt, const char *value) if (list == NULL) return; -process_address_list (opt, list); +process_address_list (ctx, list); g_object_unref (list); } @@ -329,36 +329,36 @@ _count_filenames (notmuch_message_t *message) } static int -do_search_messages (search_options_t *opt) +do_search_messages (search_context_t *ctx) { notmuch_message_t *message; notmuch_messages_t *messages; notmuch_filenames_t *filenames; -sprinter_t *format = opt->format; +sprinter_t *format = ctx->format; int i; -if (opt->offset < 0) { - opt->offset += notmuch_query_count_messages (opt->query); - if (opt->offset < 0) - opt->offset = 0; +if (ctx->offset < 0) { + ctx->offset +=
[PATCH v2 00/10] "notmuch address" command
Hi all, this is v2 of "notmuch address" patchset. It obsoletes [1]. Don't be scared by the number of patches. Most of them are trivial refactoring. Patches 1-4 refactor the code so that "notmuch search" command is easier to split. Patch 5 is Jani's hierarchical command line parsing patch. Patch 6 splits search functionality to new address command. Patch 7 is minor refactoring. Patches 8-10 correspond to patches 5-7 in the original "notmuch search --output=sender/recipients" patch series [2]. Changes from v1: - Rebased to current master (conflicted with Jani's "notmuch search --duplicate=N with --output=messages" patch) - Fixed printing of false "Unrecognized option" error message in hierarchical command line parser. Regards, -Michal [1] id:1414889400-30977-1-git-send-email-sojkam1 at fel.cvut.cz [2] id:1414792441-29555-1-git-send-email-sojkam1 at fel.cvut.cz Jani Nikula (1): cli: add support for hierarchical command line option arrays Michal Sojka (9): cli: search: Rename options to context cli: search: Move more variables into search_context_t cli: search: Convert ctx. to ctx-> cli: search: Split notmuch_search_command to smaller functions cli: Introduce "notmuch address" command cli: search: Convert --output to keyword argument cli: address: Do not output duplicate addresses cli: address: Add --output=count cli: address: Add --filter-by option to configure address filtering command-line-arguments.c | 16 +- command-line-arguments.h | 1 + completion/notmuch-completion.bash | 48 +++- completion/notmuch-completion.zsh | 11 +- doc/man1/notmuch-address.rst | 140 doc/man1/notmuch-search.rst| 21 +- doc/man1/notmuch.rst | 7 +- notmuch-client.h | 3 + notmuch-search.c | 454 + notmuch.c | 2 + test/T095-address.sh | 148 test/T097-address-filter-by.sh | 73 ++ 12 files changed, 751 insertions(+), 173 deletions(-) create mode 100644 doc/man1/notmuch-address.rst create mode 100755 test/T095-address.sh create mode 100755 test/T097-address-filter-by.sh -- 2.1.1
Re: [PATCH v2 07/10] cli: search: Convert --output to keyword argument
On Tue, Nov 04 2014, Mark Walters wrote: Hi On Mon, 03 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote: Now, when address related outputs are in a separate command, it makes no sense to combine multiple --output options in search command line. Using switch statement to handle different outputs is more readable than a series of if statements. I am not keen on this change: I think the user should always be able to force the default output by setting command line options (which should protect against future changes in the default). You can do this even with this patch. Thus I would like to continue to allow --output=sender --output=recipients. This is allowed in notmuch address. This patch modifies only notmuch search. Currently it makes no sense to run --output=files --output=tags or other output combinations. I do approve of making the default do something useful but whether it should be both or just sender (which is much faster) is unclear to me. Does this comment refer to 7/10 or to 6/10? I'd say it refers to the following line from 6/10. search_context.output = OUTPUT_SENDER | OUTPUT_RECIPIENTS; Even there you can override the default by command line option. This get executed only if no --output appears on the command line. Thanks -Michal Best wishes Mark --- doc/man1/notmuch-search.rst | 3 --- notmuch-search.c| 25 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst index 65df288..0cc2911 100644 --- a/doc/man1/notmuch-search.rst +++ b/doc/man1/notmuch-search.rst @@ -78,9 +78,6 @@ Supported options for **search** include by null characters (--format=text0), as a JSON array (--format=json), or as an S-Expression list (--format=sexp). -This option can be given multiple times to combine different -outputs. - ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological diff --git a/notmuch-search.c b/notmuch-search.c index cbd84f5..402e860 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -593,7 +593,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) int opt_index, ret; notmuch_opt_desc_t options[] = { -{ NOTMUCH_OPT_KEYWORD_FLAGS, ctx-output, output, 'o', +{ NOTMUCH_OPT_KEYWORD, ctx-output, output, 'o', (notmuch_keyword_t []){ { summary, OUTPUT_SUMMARY }, { threads, OUTPUT_THREADS }, { messages, OUTPUT_MESSAGES }, @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0, 0, 0, 0 } }; +ctx-output = OUTPUT_SUMMARY; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index 0) return EXIT_FAILURE; -if (! ctx-output) -ctx-output = OUTPUT_SUMMARY; - if (ctx-output != OUTPUT_FILES ctx-output != OUTPUT_MESSAGES ctx-dupe != -1) { fprintf (stderr, Error: --duplicate=N is only supported with --output=files and --output=messages.\n); @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -if (ctx-output == OUTPUT_SUMMARY || -ctx-output == OUTPUT_THREADS) +switch (ctx-output) { +case OUTPUT_SUMMARY: +case OUTPUT_THREADS: ret = do_search_threads (ctx); -else if (ctx-output == OUTPUT_MESSAGES || - ctx-output == OUTPUT_FILES) +break; +case OUTPUT_MESSAGES: +case OUTPUT_FILES: ret = do_search_messages (ctx); -else if (ctx-output == OUTPUT_TAGS) +break; +case OUTPUT_TAGS: ret = do_search_tags (ctx); -else { -fprintf (stderr, Error: the combination of outputs is not supported.\n); -ret = 1; +break; +default: +INTERNAL_ERROR (Unexpected output); } _notmuch_search_cleanup (ctx); -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 08/10] cli: address: Do not output duplicate addresses
On Tue, Nov 04 2014, David Bremner wrote: Michal Sojka sojk...@fel.cvut.cz writes: +/* Returns TRUE iff name and addr is duplicate. */ If you're revising this patch, it would be good to mention the side effect of this function. -process_address_list (const search_context_t *ctx, InternetAddressList *list) +process_address_list (const search_context_t *ctx, + InternetAddressList *list) It probably doesn't make any difference, but this looks like a needless whitespace change. This function definitely needs some comment / pointer to documention. And probably not to have _my in the name. +static void +_my_talloc_free_for_g_hash (void *ptr) +{ +talloc_free (ptr); +} + I don't understand the name of the next subtest +test_begin_subtest No --output +notmuch address --output=sender --output=recipients '*' OUTPUT This should be notmuch address '*' OUTPUT. I'll fix that. +# Use EXPECTED from previous subtest +test_expect_equal_file OUTPUT EXPECTED + + +test_done nitpick, extra blank lines So, AIUI, this is all of the series proposed for 0.19. Agreed. It looks close to OK to me, modulo some minor style nits. One anonymous commentator on IRC mentioned the use of module scope variables, I guess in patch 6/10. I'm not sure of a better solution, but it's true in a perfect world we wouldn't have module local state. A possible solution would be fill in common_options structure programmatically, but this would make the code much less readable. I can think of a few other solutions but none of them would fit into perfect world :) I'll send updated patches in the evening (CET timezone). Thanks -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 06/10] cli: Introduce notmuch address command
On Tue, Nov 04 2014, David Bremner wrote: Michal Sojka sojk...@fel.cvut.cz writes: +{ +local cur prev words cword split +_init_completion -s || return + +$split +case ${prev} in +--format) +COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) ) +return +;; +--output) +COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) ) +return +;; +--sort) +COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) ) +return +;; +--exclude) +COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) ) +return +;; +esac + +! $split +case ${cur} in +-*) +local options=--format= --output= --sort= --exclude= +compopt -o nospace +COMPREPLY=( $(compgen -W $options -- ${cur}) ) +;; +*) +_notmuch_search_terms +;; +esac +} + I am reminded that we have no tests for shell completion stuff, which seems pretty fragile. + +``--format=``\ (**json**\ \|\ **sexp**\ \|\ **text**\ \|\ **text0**) +Presents the results in either JSON, S-Expressions, newline +character separated plain-text (default), or null character +separated plain-text (compatible with **xargs(1)** -0 option +where available). + +``--format-version=N`` +Use the specified structured output format version. This is +intended for programs that invoke **notmuch(1)** internally. If +omitted, the latest supported version will be used. + I wonder if at some point we should have a notmuch-output-formats.7 page. +``--exclude=(true|false|all|flag)`` +A message is called excluded if it matches at least one tag in +search.tag\_exclude that does not appear explicitly in the +search terms. This option specifies whether to omit excluded +messages in the search process. Similarly for excludes. I'm ok with the duplication for now, and I can see an argument for not making the user chase references. What about using RST include directive [1] to include shared parts into more documents? [1] http://docutils.sourceforge.net/docs/ref/rst/directives.html#including-an-external-document-fragment -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 06/10] cli: Introduce notmuch address command
On Tue, Nov 04 2014, Mark Walters wrote: On Mon, 03 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote: This moves address-related functionality from search command to the new address command. The implementation shares almost all code and some command line options. Options --offset and --limit were intentionally not included in the address command, because they refer to messages numbers, which users do not see in the output. This could confuse users because, for example, they could see more addresses in the output that what was specified with --limit. This functionality can be correctly reimplemented for addresses later. I am not sure about this: we already have this anomaly for output=files say. Also I can imagine calling notmuch address --limit=1000 ... to get a bunch of recent addresses quickly and I really am wanting to look at 1000 messages, not collect 1000 addresses. I think that one of the reasons for having the new address command is to have cleaner user interface. And including anomalies doesn't sound like a way to achieve this. I think that now you can use date: query to limit the search. I volunteer to implement address --limit properly after 0.19. This should be easy. -Michal Additionally, the 1000 message approach makes sense when we start deduping whereas 1000 authors becomes unclear. This was inspired by a patch from Jani Nikula. --- completion/notmuch-completion.bash | 42 ++- completion/notmuch-completion.zsh | 10 +++- doc/man1/notmuch-address.rst | 99 doc/man1/notmuch-search.rst| 20 +--- doc/man1/notmuch.rst | 7 +-- notmuch-client.h | 3 ++ notmuch-search.c | 101 + notmuch.c | 2 + 8 files changed, 228 insertions(+), 56 deletions(-) create mode 100644 doc/man1/notmuch-address.rst diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cfbd389..94ea2d5 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) -COMPREPLY=( $( compgen -W summary threads messages files tags sender recipients -- ${cur} ) ) +COMPREPLY=( $( compgen -W summary threads messages files tags -- ${cur} ) ) return ;; --sort) @@ -320,6 +320,44 @@ _notmuch_search() esac } +_notmuch_address() +{ +local cur prev words cword split +_init_completion -s || return + +$split +case ${prev} in +--format) +COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) ) +return +;; +--output) +COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) ) +return +;; +--sort) +COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) ) +return +;; +--exclude) +COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) ) +return +;; +esac + +! $split +case ${cur} in +-*) +local options=--format= --output= --sort= --exclude= +compopt -o nospace +COMPREPLY=( $(compgen -W $options -- ${cur}) ) +;; +*) +_notmuch_search_terms +;; +esac +} + _notmuch_show() { local cur prev words cword split @@ -393,7 +431,7 @@ _notmuch_tag() _notmuch() { -local _notmuch_commands=compact config count dump help insert new reply restore search setup show tag +local _notmuch_commands=compact config count dump help insert new reply restore search address setup show tag local arg cur prev words cword split # require bash-completion with _init_completion diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 3e52a00..c606b75 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -10,6 +10,7 @@ _notmuch_commands() 'setup:interactively set up notmuch for first use' 'new:find and import any new message to the database' 'search:search for messages matching the search terms, display matching threads as results' +'address:get addresses from messages matching the given search terms' 'reply:constructs a reply template for a set of messages' 'show:show all messages matching the search terms' 'tag:add or remove tags for all messages matching the search terms' @@ -53,7 +54,14 @@ _notmuch_search() '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ '--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order
[PATCH v3 03/10] cli: search: Convert ctx. to ctx-
In the next commit, notmuch_search_command will be refactored to several smaller functions. In order to simplify the next commit to verbatim move of several lines to new functions with search_context_t* argument, we convert all references to this structure to pointer dereferences. To do so we rename the context variable and use the original name ctx as the pointer to the renamed structure. --- notmuch-search.c | 81 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 3d2012b..6765a16 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -474,7 +474,7 @@ do_search_tags (const search_context_t *ctx) int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -search_context_t ctx = { +search_context_t search_context = { .format_sel = NOTMUCH_FORMAT_TEXT, .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, @@ -483,23 +483,24 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .limit = -1, /* unlimited */ .dupe = -1, }; +search_context_t *ctx = search_context; char *query_str; int opt_index, ret; unsigned int i; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD, ctx.sort, sort, 's', + { NOTMUCH_OPT_KEYWORD, ctx-sort, sort, 's', (notmuch_keyword_t []){ { oldest-first, NOTMUCH_SORT_OLDEST_FIRST }, { newest-first, NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, ctx.format_sel, format, 'f', + { NOTMUCH_OPT_KEYWORD, ctx-format_sel, format, 'f', (notmuch_keyword_t []){ { json, NOTMUCH_FORMAT_JSON }, { sexp, NOTMUCH_FORMAT_SEXP }, { text, NOTMUCH_FORMAT_TEXT }, { text0, NOTMUCH_FORMAT_TEXT0 }, { 0, 0 } } }, { NOTMUCH_OPT_INT, notmuch_format_version, format-version, 0, 0 }, - { NOTMUCH_OPT_KEYWORD_FLAGS, ctx.output, output, 'o', + { NOTMUCH_OPT_KEYWORD_FLAGS, ctx-output, output, 'o', (notmuch_keyword_t []){ { summary, OUTPUT_SUMMARY }, { threads, OUTPUT_THREADS }, { messages, OUTPUT_MESSAGES }, @@ -508,15 +509,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { files, OUTPUT_FILES }, { tags, OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, ctx.exclude, exclude, 'x', +{ NOTMUCH_OPT_KEYWORD, ctx-exclude, exclude, 'x', (notmuch_keyword_t []){ { true, NOTMUCH_EXCLUDE_TRUE }, { false, NOTMUCH_EXCLUDE_FALSE }, { flag, NOTMUCH_EXCLUDE_FLAG }, { all, NOTMUCH_EXCLUDE_ALL }, { 0, 0 } } }, - { NOTMUCH_OPT_INT, ctx.offset, offset, 'O', 0 }, - { NOTMUCH_OPT_INT, ctx.limit, limit, 'L', 0 }, - { NOTMUCH_OPT_INT, ctx.dupe, duplicate, 'D', 0 }, + { NOTMUCH_OPT_INT, ctx-offset, offset, 'O', 0 }, + { NOTMUCH_OPT_INT, ctx-limit, limit, 'L', 0 }, + { NOTMUCH_OPT_INT, ctx-dupe, duplicate, 'D', 0 }, { 0, 0, 0, 0, 0 } }; @@ -524,31 +525,31 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index 0) return EXIT_FAILURE; -if (! ctx.output) - ctx.output = OUTPUT_SUMMARY; +if (! ctx-output) + ctx-output = OUTPUT_SUMMARY; -if (ctx.output != OUTPUT_FILES ctx.output != OUTPUT_MESSAGES - ctx.dupe != -1) { +if (ctx-output != OUTPUT_FILES ctx-output != OUTPUT_MESSAGES + ctx-dupe != -1) { fprintf (stderr, Error: --duplicate=N is only supported with --output=files and --output=messages.\n); return EXIT_FAILURE; } -switch (ctx.format_sel) { +switch (ctx-format_sel) { case NOTMUCH_FORMAT_TEXT: - ctx.format = sprinter_text_create (config, stdout); + ctx-format = sprinter_text_create (config, stdout); break; case NOTMUCH_FORMAT_TEXT0: - if (ctx.output == OUTPUT_SUMMARY) { + if (ctx-output == OUTPUT_SUMMARY) { fprintf (stderr, Error: --format=text0 is not compatible with --output=summary.\n); return EXIT_FAILURE; } - ctx.format = sprinter_text0_create (config, stdout); + ctx-format = sprinter_text0_create (config, stdout); break; case NOTMUCH_FORMAT_JSON: - ctx.format = sprinter_json_create (config, stdout); + ctx-format = sprinter_json_create (config, stdout); break; case NOTMUCH_FORMAT_SEXP: - ctx.format = sprinter_sexp_create
[PATCH v3 00/10] notmuch address command
Hi all, this is v3 of notmuch address patchset. It obsoletes [1]. I think I addressed all comments made to v2. The diff between v2 and v3 is below. Besides this, I improved commit messages. I also tried to get rid of global variables in 6/10, but it looked ugly, because the definition of keywords (e.g. text, json, ...) had to be outside of option definition. So I kept the code as it was in v2. It seems that the agreement is to merge patches 1-9 for 0.19 and leave patch 10 for further discussion. Thanks -Michal [1] id:1415058622-21162-1-git-send-email-sojk...@fel.cvut.cz Jani Nikula (1): cli: add support for hierarchical command line option arrays Michal Sojka (9): cli: search: Rename options to context cli: search: Move more variables into search_context_t cli: search: Convert ctx. to ctx- cli: search: Split notmuch_search_command to smaller functions cli: Introduce notmuch address command cli: search: Convert --output to keyword argument cli: address: Do not output duplicate addresses cli: address: Add --output=count cli: address: Add --filter-by option to configure address filtering command-line-arguments.c | 16 +- command-line-arguments.h | 1 + completion/notmuch-completion.bash | 48 +++- completion/notmuch-completion.zsh | 11 +- doc/man1/notmuch-address.rst | 132 +++ doc/man1/notmuch-search.rst| 21 +- doc/man1/notmuch.rst | 7 +- notmuch-client.h | 3 + notmuch-search.c | 461 ++--- notmuch.c | 2 + test/T095-address.sh | 148 test/T097-address-filter-by.sh | 73 ++ 12 files changed, 750 insertions(+), 173 deletions(-) create mode 100644 doc/man1/notmuch-address.rst create mode 100755 test/T095-address.sh create mode 100755 test/T097-address-filter-by.sh -- 2.1.1 diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 524ab91..00582c3 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -30,7 +30,7 @@ Supported options for **address** include intended for programs that invoke **notmuch(1)** internally. If omitted, the latest supported version will be used. -``--output=(sender|recipients)`` +``--output=(sender|recipients|count)`` Controls which information appears in the output. This option can be given multiple times to combine different outputs. @@ -64,7 +64,9 @@ Supported options for **address** include By default, results will be displayed in reverse chronological order, (that is, the newest results will be displayed first). -``--exclude=(true|false|all|flag)`` + This option has no effect when used with --output=count. + +``--exclude=(true|false)`` A message is called excluded if it matches at least one tag in search.tag\_exclude that does not appear explicitly in the search terms. This option specifies whether to omit excluded @@ -73,18 +75,8 @@ Supported options for **address** include The default value, **true**, prevents excluded messages from matching the search terms. -**all** additionally prevents excluded messages from appearing -in displayed results, in effect behaving as though the excluded -messages do not exist. - **false** allows excluded messages to match search terms and -appear in displayed results. Excluded messages are still marked -in the relevant outputs. - -**flag** only has an effect when ``--output=summary``. The -output is almost identical to **false**, but the match count -is the number of matching non-excluded messages in the thread, -rather than the number of matching messages. +appear in displayed results. ``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ **addrfold**\ \|\ **nameaddrfold**\) diff --git a/notmuch-search.c b/notmuch-search.c index 04e33c6..246ec0a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -252,7 +252,9 @@ do_search_threads (search_context_t *ctx) return 0; } -/* Returns TRUE iff name and/or addr is considered duplicate. */ +/* Returns TRUE iff name and/or addr is considered duplicate. If not, + * stores the name/addr pair in order to detect subsequent + * duplicates. */ static notmuch_bool_t is_duplicate (const search_context_t *ctx, const char *name, const char *addr) { @@ -405,8 +407,9 @@ process_address_header (const search_context_t *ctx, const char *value) g_object_unref (list); } +/* Destructor for talloc-allocated GHashTable keys and values. */ static void -_my_talloc_free_for_g_hash (void *ptr) +_talloc_free_for_g_hash (void *ptr) { talloc_free (ptr); } @@ -679,12 +682,6 @@ static const notmuch_opt_desc_t common_options[] = { { text0, NOTMUCH_FORMAT_TEXT0
[PATCH v3 07/10] cli: search: Convert --output to keyword argument
Now, when address related outputs are in a separate command, it makes no sense to combine multiple --output options in search command line. Using switch statement to handle different outputs is more readable than a series of if statements. --- doc/man1/notmuch-search.rst | 3 --- notmuch-search.c| 25 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst index 65df288..0cc2911 100644 --- a/doc/man1/notmuch-search.rst +++ b/doc/man1/notmuch-search.rst @@ -78,9 +78,6 @@ Supported options for **search** include by null characters (--format=text0), as a JSON array (--format=json), or as an S-Expression list (--format=sexp). - This option can be given multiple times to combine different - outputs. - ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological diff --git a/notmuch-search.c b/notmuch-search.c index 69938d6..e084d25 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -587,7 +587,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) int opt_index, ret; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD_FLAGS, ctx-output, output, 'o', + { NOTMUCH_OPT_KEYWORD, ctx-output, output, 'o', (notmuch_keyword_t []){ { summary, OUTPUT_SUMMARY }, { threads, OUTPUT_THREADS }, { messages, OUTPUT_MESSAGES }, @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0, 0, 0, 0 } }; +ctx-output = OUTPUT_SUMMARY; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index 0) return EXIT_FAILURE; -if (! ctx-output) - ctx-output = OUTPUT_SUMMARY; - if (ctx-output != OUTPUT_FILES ctx-output != OUTPUT_MESSAGES ctx-dupe != -1) { fprintf (stderr, Error: --duplicate=N is only supported with --output=files and --output=messages.\n); @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -if (ctx-output == OUTPUT_SUMMARY || - ctx-output == OUTPUT_THREADS) +switch (ctx-output) { +case OUTPUT_SUMMARY: +case OUTPUT_THREADS: ret = do_search_threads (ctx); -else if (ctx-output == OUTPUT_MESSAGES || -ctx-output == OUTPUT_FILES) + break; +case OUTPUT_MESSAGES: +case OUTPUT_FILES: ret = do_search_messages (ctx); -else if (ctx-output == OUTPUT_TAGS) + break; +case OUTPUT_TAGS: ret = do_search_tags (ctx); -else { - fprintf (stderr, Error: the combination of outputs is not supported.\n); - ret = 1; + break; +default: + INTERNAL_ERROR (Unexpected output); } _notmuch_search_cleanup (ctx); -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 05/10] cli: add support for hierarchical command line option arrays
From: Jani Nikula j...@nikula.org NOTMUCH_OPT_INHERIT expects a notmuch_opt_desc_t * pointer in output_var. The Unrecognized option message was moved out of parse_option() to not be emitted twice or when parsing a non-inherited option. --- command-line-arguments.c | 16 +--- command-line-arguments.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/command-line-arguments.c b/command-line-arguments.c index c6f7269..de6b453 100644 --- a/command-line-arguments.c +++ b/command-line-arguments.c @@ -122,16 +122,18 @@ parse_position_arg (const char *arg_str, int pos_arg_index, */ notmuch_bool_t -parse_option (const char *arg, - const notmuch_opt_desc_t *options) { - -assert(arg); +parse_option (const char *_arg, const notmuch_opt_desc_t *options) +{ +assert(_arg); assert(options); -arg += 2; - +const char *arg = _arg + 2; /* _arg starts with -- */ const notmuch_opt_desc_t *try; for (try = options; try-opt_type != NOTMUCH_OPT_END; try++) { + if (try-opt_type == NOTMUCH_OPT_INHERIT + parse_option (_arg, try-output_var)) + return TRUE; + if (! try-name) continue; @@ -170,7 +172,6 @@ parse_option (const char *arg, /*UNREACHED*/ } } -fprintf (stderr, Unrecognized option: --%s\n, arg); return FALSE; } @@ -201,6 +202,7 @@ parse_arguments (int argc, char **argv, if (more_args) { opt_index++; } else { + fprintf (stderr, Unrecognized option: %s\n, argv[opt_index]); opt_index = -1; } diff --git a/command-line-arguments.h b/command-line-arguments.h index 6444129..309aaf2 100644 --- a/command-line-arguments.h +++ b/command-line-arguments.h @@ -5,6 +5,7 @@ enum notmuch_opt_type { NOTMUCH_OPT_END = 0, +NOTMUCH_OPT_INHERIT, /* another options table */ NOTMUCH_OPT_BOOLEAN, /* --verbose */ NOTMUCH_OPT_INT, /* --frob=8 */ NOTMUCH_OPT_KEYWORD, /* --format=raw|json|text */ -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering
This option allows to configure the criterion for duplicate address filtering. Without this option, all unique combinations of name and address parts are printed. This option allows to filter the output more, for example to only contain unique address parts. --- completion/notmuch-completion.bash | 6 +++- completion/notmuch-completion.zsh | 1 + doc/man1/notmuch-address.rst | 36 ++- notmuch-search.c | 51 -- test/T097-address-filter-by.sh | 73 ++ 5 files changed, 162 insertions(+), 5 deletions(-) create mode 100755 test/T097-address-filter-by.sh diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index db152f3..2cb1586 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -310,7 +310,7 @@ _notmuch_search() ! $split case ${cur} in -*) - local options=--format= --output= --sort= --offset= --limit= --exclude= --duplicate= + local options=--format= --output= --sort= --offset= --limit= --exclude= --duplicate= --filter-by= compopt -o nospace COMPREPLY=( $(compgen -W $options -- ${cur}) ) ;; @@ -343,6 +343,10 @@ _notmuch_address() COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) ) return ;; + --filter-by) + COMPREPLY=( $( compgen -W nameaddr name addr addrfold nameaddrfold -- ${cur} ) ) + return + ;; esac ! $split diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 8968562..3758f1a 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -62,6 +62,7 @@ _notmuch_address() _arguments -s : \ '--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ '--output=[select what to output]:output:((sender recipients count))' +'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:both name and address part name\:name part addr\:address part addrfold\:case-insensitive address part nameaddrfold\:name and case-insensitive address part))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 359616e..00582c3 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -11,7 +11,8 @@ DESCRIPTION === Search for messages matching the given search terms, and display the -addresses from them. Duplicate addresses are filtered out. +addresses from them. Duplicate addresses are filtered out. Filtering +can be configured with the --filter-by option. See **notmuch-search-terms(7)** for details of the supported syntax for search-terms. @@ -77,6 +78,39 @@ Supported options for **address** include **false** allows excluded messages to match search terms and appear in displayed results. +``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ **addrfold**\ \|\ **nameaddrfold**\) + + Controls how to filter out duplicate addresses. The filtering + algorithm receives a sequence of email addresses and outputs + the same sequence without the addresses that are considered a + duplicate of a previously output address. What is considered a + duplicate depends on how the two addresses are compared: + + **nameaddr** means that both name and address parts are + compared in case-sensitive manner. Therefore, all same looking + addresses strings are considered duplicate. This is the + default. + + **name** means that only the name part is compared (in + case-sensitive manner). For example, the addresses John Doe + m...@example.com and John Doe j...@doe.name will be + considered duplicate. + + **addr** means that only the address part is compared (in + case-sensitive manner). For example, the addresses John Doe + j...@example.com and Dr. John Doe j...@example.com will + be considered duplicate. + + **addrfold** is like **addr**, but comparison is done in + canse-insensitive manner. For example, the addresses John Doe + j...@example.com and Dr. John Doe j...@example.com will + be considered duplicate. + + **nameaddrfold** is like **nameaddr**, but address comparison + is done in canse-insensitive manner. For example, the + addresses John Doe j...@example.com and John Doe + j...@example.com will be considered duplicate. + EXIT STATUS === diff --git a/notmuch-search.c b/notmuch-search.c index 5036d8e..246ec0a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -43,6 +43,14 @@ typedef enum { NOTMUCH_FORMAT_SEXP } format_sel_t; +typedef enum { +FILTER_BY_NAMEADDR = 0, +FILTER_BY_NAME, +FILTER_BY_ADDR, +FILTER_BY_ADDRFOLD, +FILTER_BY_NAMEADDRFOLD, +}
[PATCH v3 02/10] cli: search: Move more variables into search_context_t
In order to share some command line options between search and address subcommands we need to add corresponding variables to the context structure. While we are at it, we also add notmuch_database_t to unify parameters of all do_search_* functions and to simplify subsequent commits. Otherwise, there are no functional changes. --- notmuch-search.c | 49 ++--- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 2c47b80..3d2012b 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -34,8 +34,18 @@ typedef enum { #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS) +typedef enum { +NOTMUCH_FORMAT_JSON, +NOTMUCH_FORMAT_TEXT, +NOTMUCH_FORMAT_TEXT0, +NOTMUCH_FORMAT_SEXP +} format_sel_t; + typedef struct { +notmuch_database_t *notmuch; +format_sel_t format_sel; sprinter_t *format; +notmuch_exclude_t exclude; notmuch_query_t *query; notmuch_sort_t sort; output_t output; @@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx) } static int -do_search_tags (notmuch_database_t *notmuch, - const search_context_t *ctx) +do_search_tags (const search_context_t *ctx) { notmuch_messages_t *messages = NULL; notmuch_tags_t *tags; const char *tag; sprinter_t *format = ctx-format; notmuch_query_t *query = ctx-query; +notmuch_database_t *notmuch = ctx-notmuch; /* should the following only special case if no excluded terms * specified? */ @@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch, int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -notmuch_database_t *notmuch; search_context_t ctx = { + .format_sel = NOTMUCH_FORMAT_TEXT, + .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, .output = 0, .offset = 0, @@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) }; char *query_str; int opt_index, ret; -notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE; unsigned int i; -enum { - NOTMUCH_FORMAT_JSON, - NOTMUCH_FORMAT_TEXT, - NOTMUCH_FORMAT_TEXT0, - NOTMUCH_FORMAT_SEXP -} format_sel = NOTMUCH_FORMAT_TEXT; - notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, ctx.sort, sort, 's', (notmuch_keyword_t []){ { oldest-first, NOTMUCH_SORT_OLDEST_FIRST }, { newest-first, NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, format_sel, format, 'f', + { NOTMUCH_OPT_KEYWORD, ctx.format_sel, format, 'f', (notmuch_keyword_t []){ { json, NOTMUCH_FORMAT_JSON }, { sexp, NOTMUCH_FORMAT_SEXP }, { text, NOTMUCH_FORMAT_TEXT }, @@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { files, OUTPUT_FILES }, { tags, OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, exclude, exclude, 'x', +{ NOTMUCH_OPT_KEYWORD, ctx.exclude, exclude, 'x', (notmuch_keyword_t []){ { true, NOTMUCH_EXCLUDE_TRUE }, { false, NOTMUCH_EXCLUDE_FALSE }, { flag, NOTMUCH_EXCLUDE_FLAG }, @@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (format_sel) { +switch (ctx.format_sel) { case NOTMUCH_FORMAT_TEXT: ctx.format = sprinter_text_create (config, stdout); break; @@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_exit_if_unsupported_format (); if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, notmuch)) + NOTMUCH_DATABASE_MODE_READ_ONLY, ctx.notmuch)) return EXIT_FAILURE; -query_str = query_string_from_args (notmuch, argc-opt_index, argv+opt_index); +query_str = query_string_from_args (ctx.notmuch, argc-opt_index, argv+opt_index); if (query_str == NULL) { fprintf (stderr, Out of memory.\n); return EXIT_FAILURE; @@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -ctx.query = notmuch_query_create (notmuch, query_str); +ctx.query = notmuch_query_create (ctx.notmuch, query_str); if (ctx.query == NULL) { fprintf (stderr, Out of memory\n); return EXIT_FAILURE; @@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_query_set_sort
[PATCH v3 08/10] cli: address: Do not output duplicate addresses
This filters out duplicate addresses from address command output. It also also adds tests for the address command. The code here is an extended version of a patch from Jani Nikula. --- doc/man1/notmuch-address.rst | 2 +- notmuch-search.c | 42 ++- test/T095-address.sh | 99 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100755 test/T095-address.sh diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index d349237..01eb811 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -11,7 +11,7 @@ DESCRIPTION === Search for messages matching the given search terms, and display the -addresses from them. +addresses from them. Duplicate addresses are filtered out. See **notmuch-search-terms(7)** for details of the supported syntax for search-terms. diff --git a/notmuch-search.c b/notmuch-search.c index e084d25..86d54ba 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -53,6 +53,7 @@ typedef struct { int offset; int limit; int dupe; +GHashTable *addresses; } search_context_t; typedef struct { @@ -240,6 +241,28 @@ do_search_threads (search_context_t *ctx) return 0; } +/* Returns TRUE iff name and addr is duplicate. If not, stores the + * name/addr pair in order to detect subsequent duplicates. */ +static notmuch_bool_t +is_duplicate (const search_context_t *ctx, const char *name, const char *addr) +{ +notmuch_bool_t duplicate; +char *key; + +key = talloc_asprintf (ctx-format, %s %s, name, addr); +if (! key) + return FALSE; + +duplicate = g_hash_table_lookup_extended (ctx-addresses, key, NULL, NULL); + +if (! duplicate) + g_hash_table_insert (ctx-addresses, key, NULL); +else + talloc_free (key); + +return duplicate; +} + static void print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { @@ -274,7 +297,8 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) /* Print addresses from InternetAddressList. */ static void -process_address_list (const search_context_t *ctx, InternetAddressList *list) +process_address_list (const search_context_t *ctx, + InternetAddressList *list) { InternetAddress *address; int i; @@ -298,6 +322,9 @@ process_address_list (const search_context_t *ctx, InternetAddressList *list) .addr = internet_address_mailbox_get_addr (mailbox), }; + if (is_duplicate (ctx, mbx.name, mbx.addr)) + continue; + print_mailbox (ctx, mbx); } } @@ -321,6 +348,13 @@ process_address_header (const search_context_t *ctx, const char *value) g_object_unref (list); } +/* Destructor for talloc-allocated GHashTable keys and values. */ +static void +_talloc_free_for_g_hash (void *ptr) +{ +talloc_free (ptr); +} + static int _count_filenames (notmuch_message_t *message) { @@ -673,8 +707,14 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]) argc - opt_index, argv + opt_index)) return EXIT_FAILURE; +ctx-addresses = g_hash_table_new_full (g_str_hash, g_str_equal, + _talloc_free_for_g_hash, NULL); + ret = do_search_messages (ctx); +g_hash_table_unref (ctx-addresses); + + _notmuch_search_cleanup (ctx); return ret ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/test/T095-address.sh b/test/T095-address.sh new file mode 100755 index 000..0d47c0d --- /dev/null +++ b/test/T095-address.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +test_description='notmuch address in several variants' +. ./test-lib.sh + +add_email_corpus + +test_begin_subtest --output=sender +notmuch address --output=sender '*' OUTPUT +cat EOF EXPECTED +François Boulogne boulogn...@gmail.com +Olivier Berger olivier.ber...@it-sudparis.eu +Chris Wilson ch...@chris-wilson.co.uk +Carl Worth cwo...@cworth.org +Alexander Botero-Lowry alex.boterolo...@gmail.com +Keith Packard kei...@keithp.com +Jjgod Jiang gzjj...@gmail.com +Rolland Santimano rollandsantim...@yahoo.com +Jan Janak j...@ryngle.com +Stewart Smith stew...@flamingspork.com +Lars Kellogg-Stedman l...@seas.harvard.edu +Alex Botero-Lowry alex.boterolo...@gmail.com +Ingmar Vanhassel ing...@exherbo.org +Aron Griffis agrif...@n01se.net +Adrian Perez de Castro ape...@igalia.com +Israel Herraiz i...@herraiz.org +Mikhail Gusarov dotted...@dottedmag.net +EOF +test_expect_equal_file OUTPUT EXPECTED + +test_begin_subtest --output=sender --format=json +notmuch address --output=sender --format=json '*' OUTPUT +cat EOF EXPECTED +[{name: François Boulogne, address: boulogn...@gmail.com, name-addr: François Boulogne boulogn...@gmail.com}, +{name: Olivier Berger, address: olivier.ber...@it-sudparis.eu, name-addr: Olivier Berger olivier.ber...@it-sudparis.eu}, +{name: Chris Wilson,
[PATCH v3 09/10] cli: address: Add --output=count
This output prints how many times was each address encountered during search. --- completion/notmuch-completion.bash | 2 +- completion/notmuch-completion.zsh | 2 +- doc/man1/notmuch-address.rst | 11 - notmuch-search.c | 49 -- test/T095-address.sh | 49 ++ 5 files changed, 103 insertions(+), 10 deletions(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 94ea2d5..db152f3 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -332,7 +332,7 @@ _notmuch_address() return ;; --output) - COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) ) + COMPREPLY=( $( compgen -W sender recipients count -- ${cur} ) ) return ;; --sort) diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index c606b75..8968562 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -61,7 +61,7 @@ _notmuch_address() { _arguments -s : \ '--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ -'--output=[select what to output]:output:((sender recipients))' +'--output=[select what to output]:output:((sender recipients count))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst index 01eb811..359616e 100644 --- a/doc/man1/notmuch-address.rst +++ b/doc/man1/notmuch-address.rst @@ -29,7 +29,7 @@ Supported options for **address** include intended for programs that invoke **notmuch(1)** internally. If omitted, the latest supported version will be used. -``--output=(sender|recipients)`` +``--output=(sender|recipients|count)`` Controls which information appears in the output. This option can be given multiple times to combine different outputs. @@ -48,6 +48,13 @@ Supported options for **address** include Output all addresses from the *To*, *Cc* and *Bcc* headers. + **count** + Print the count of how many times was the address + encountered during search. + + Note: With this option, addresses are printed only after + the whole search is finished. This may take long time. + ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological @@ -56,6 +63,8 @@ Supported options for **address** include By default, results will be displayed in reverse chronological order, (that is, the newest results will be displayed first). + This option has no effect when used with --output=count. + ``--exclude=(true|false)`` A message is called excluded if it matches at least one tag in search.tag\_exclude that does not appear explicitly in the diff --git a/notmuch-search.c b/notmuch-search.c index 86d54ba..5036d8e 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -33,6 +33,7 @@ typedef enum { /* Address command */ OUTPUT_SENDER = 1 5, OUTPUT_RECIPIENTS = 1 6, +OUTPUT_COUNT = 1 7, } output_t; typedef enum { @@ -59,6 +60,7 @@ typedef struct { typedef struct { const char *name; const char *addr; +int count; } mailbox_t; /* Return two stable query strings that identify exactly the matched @@ -248,17 +250,24 @@ is_duplicate (const search_context_t *ctx, const char *name, const char *addr) { notmuch_bool_t duplicate; char *key; +mailbox_t *mailbox; key = talloc_asprintf (ctx-format, %s %s, name, addr); if (! key) return FALSE; -duplicate = g_hash_table_lookup_extended (ctx-addresses, key, NULL, NULL); +duplicate = g_hash_table_lookup_extended (ctx-addresses, key, NULL, (gpointer)mailbox); -if (! duplicate) - g_hash_table_insert (ctx-addresses, key, NULL); -else +if (! duplicate) { + mailbox = talloc (ctx-format, mailbox_t); + mailbox-name = talloc_strdup (mailbox, name); + mailbox-addr = talloc_strdup (mailbox, addr); + mailbox-count = 1; + g_hash_table_insert (ctx-addresses, key, mailbox); +} else { + mailbox-count++; talloc_free (key); +} return duplicate; } @@ -268,6 +277,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { const char *name = mailbox-name; const char *addr = mailbox-addr; +int count = mailbox-count; sprinter_t *format = ctx-format; InternetAddress *ia = internet_address_mailbox_new (name, addr); char *name_addr; @@ -277,6 +287,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) name_addr = internet_address_to_string (ia, FALSE);
[PATCH v3 04/10] cli: search: Split notmuch_search_command to smaller functions
In a subsequent commit, these functions will be used to share some functionality between search and address commands. --- notmuch-search.c | 155 ++- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6765a16..f115359 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx) return 0; } +static int +_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int argc, char *argv[]) +{ +char *query_str; +unsigned int i; + +switch (ctx-format_sel) { +case NOTMUCH_FORMAT_TEXT: + ctx-format = sprinter_text_create (config, stdout); + break; +case NOTMUCH_FORMAT_TEXT0: + if (ctx-output == OUTPUT_SUMMARY) { + fprintf (stderr, Error: --format=text0 is not compatible with --output=summary.\n); + return EXIT_FAILURE; + } + ctx-format = sprinter_text0_create (config, stdout); + break; +case NOTMUCH_FORMAT_JSON: + ctx-format = sprinter_json_create (config, stdout); + break; +case NOTMUCH_FORMAT_SEXP: + ctx-format = sprinter_sexp_create (config, stdout); + break; +default: + /* this should never happen */ + INTERNAL_ERROR(no output format selected); +} + +notmuch_exit_if_unsupported_format (); + +if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch)) + return EXIT_FAILURE; + +query_str = query_string_from_args (ctx-notmuch, argc, argv); +if (query_str == NULL) { + fprintf (stderr, Out of memory.\n); + return EXIT_FAILURE; +} +if (*query_str == '\0') { + fprintf (stderr, Error: notmuch search requires at least one search term.\n); + return EXIT_FAILURE; +} + +ctx-query = notmuch_query_create (ctx-notmuch, query_str); +if (ctx-query == NULL) { + fprintf (stderr, Out of memory\n); + return EXIT_FAILURE; +} + +notmuch_query_set_sort (ctx-query, ctx-sort); + +if (ctx-exclude == NOTMUCH_EXCLUDE_FLAG ctx-output != OUTPUT_SUMMARY) { + /* If we are not doing summary output there is nowhere to +* print the excluded flag so fall back on including the +* excluded messages. */ + fprintf (stderr, Warning: this output format cannot flag excluded messages.\n); + ctx-exclude = NOTMUCH_EXCLUDE_FALSE; +} + +if (ctx-exclude != NOTMUCH_EXCLUDE_FALSE) { + const char **search_exclude_tags; + size_t search_exclude_tags_length; + + search_exclude_tags = notmuch_config_get_search_exclude_tags + (config, search_exclude_tags_length); + for (i = 0; i search_exclude_tags_length; i++) + notmuch_query_add_tag_exclude (ctx-query, search_exclude_tags[i]); + notmuch_query_set_omit_excluded (ctx-query, ctx-exclude); +} + +return 0; +} + +static void +_notmuch_search_cleanup (search_context_t *ctx) +{ +notmuch_query_destroy (ctx-query); +notmuch_database_destroy (ctx-notmuch); + +talloc_free (ctx-format); +} + int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { @@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .dupe = -1, }; search_context_t *ctx = search_context; -char *query_str; int opt_index, ret; -unsigned int i; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, ctx-sort, sort, 's', @@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (ctx-format_sel) { -case NOTMUCH_FORMAT_TEXT: - ctx-format = sprinter_text_create (config, stdout); - break; -case NOTMUCH_FORMAT_TEXT0: - if (ctx-output == OUTPUT_SUMMARY) { - fprintf (stderr, Error: --format=text0 is not compatible with --output=summary.\n); - return EXIT_FAILURE; - } - ctx-format = sprinter_text0_create (config, stdout); - break; -case NOTMUCH_FORMAT_JSON: - ctx-format = sprinter_json_create (config, stdout); - break; -case NOTMUCH_FORMAT_SEXP: - ctx-format = sprinter_sexp_create (config, stdout); - break; -default: - /* this should never happen */ - INTERNAL_ERROR(no output format selected); -} - -notmuch_exit_if_unsupported_format (); - -if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch)) +if (_notmuch_search_prepare (ctx, config, +argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -query_str = query_string_from_args (ctx-notmuch, argc-opt_index, argv+opt_index); -if (query_str == NULL) { -
[PATCH v3 01/10] cli: search: Rename options to context
In the next commit the options structure will be extended by non-option variables. Therefore we need a more generic name. Just text replacement, no other changes. --- notmuch-search.c | 142 +++ 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6345fb6..2c47b80 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -42,7 +42,7 @@ typedef struct { int offset; int limit; int dupe; -} search_options_t; +} search_context_t; typedef struct { const char *name; @@ -89,39 +89,39 @@ get_thread_query (notmuch_thread_t *thread, } static int -do_search_threads (search_options_t *opt) +do_search_threads (search_context_t *ctx) { notmuch_thread_t *thread; notmuch_threads_t *threads; notmuch_tags_t *tags; -sprinter_t *format = opt-format; +sprinter_t *format = ctx-format; time_t date; int i; -if (opt-offset 0) { - opt-offset += notmuch_query_count_threads (opt-query); - if (opt-offset 0) - opt-offset = 0; +if (ctx-offset 0) { + ctx-offset += notmuch_query_count_threads (ctx-query); + if (ctx-offset 0) + ctx-offset = 0; } -threads = notmuch_query_search_threads (opt-query); +threads = notmuch_query_search_threads (ctx-query); if (threads == NULL) return 1; format-begin_list (format); for (i = 0; -notmuch_threads_valid (threads) (opt-limit 0 || i opt-offset + opt-limit); +notmuch_threads_valid (threads) (ctx-limit 0 || i ctx-offset + ctx-limit); notmuch_threads_move_to_next (threads), i++) { thread = notmuch_threads_get (threads); - if (i opt-offset) { + if (i ctx-offset) { notmuch_thread_destroy (thread); continue; } - if (opt-output == OUTPUT_THREADS) { + if (ctx-output == OUTPUT_THREADS) { format-set_prefix (format, thread); format-string (format, notmuch_thread_get_thread_id (thread)); @@ -138,7 +138,7 @@ do_search_threads (search_options_t *opt) format-begin_map (format); - if (opt-sort == NOTMUCH_SORT_OLDEST_FIRST) + if (ctx-sort == NOTMUCH_SORT_OLDEST_FIRST) date = notmuch_thread_get_oldest_date (thread); else date = notmuch_thread_get_newest_date (thread); @@ -230,11 +230,11 @@ do_search_threads (search_options_t *opt) } static void -print_mailbox (const search_options_t *opt, const mailbox_t *mailbox) +print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox) { const char *name = mailbox-name; const char *addr = mailbox-addr; -sprinter_t *format = opt-format; +sprinter_t *format = ctx-format; InternetAddress *ia = internet_address_mailbox_new (name, addr); char *name_addr; @@ -263,7 +263,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t *mailbox) /* Print addresses from InternetAddressList. */ static void -process_address_list (const search_options_t *opt, InternetAddressList *list) +process_address_list (const search_context_t *ctx, InternetAddressList *list) { InternetAddress *address; int i; @@ -279,7 +279,7 @@ process_address_list (const search_options_t *opt, InternetAddressList *list) if (group_list == NULL) continue; - process_address_list (opt, group_list); + process_address_list (ctx, group_list); } else { InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address); mailbox_t mbx = { @@ -287,14 +287,14 @@ process_address_list (const search_options_t *opt, InternetAddressList *list) .addr = internet_address_mailbox_get_addr (mailbox), }; - print_mailbox (opt, mbx); + print_mailbox (ctx, mbx); } } } /* Print addresses from a message header. */ static void -process_address_header (const search_options_t *opt, const char *value) +process_address_header (const search_context_t *ctx, const char *value) { InternetAddressList *list; @@ -305,7 +305,7 @@ process_address_header (const search_options_t *opt, const char *value) if (list == NULL) return; -process_address_list (opt, list); +process_address_list (ctx, list); g_object_unref (list); } @@ -329,36 +329,36 @@ _count_filenames (notmuch_message_t *message) } static int -do_search_messages (search_options_t *opt) +do_search_messages (search_context_t *ctx) { notmuch_message_t *message; notmuch_messages_t *messages; notmuch_filenames_t *filenames; -sprinter_t *format = opt-format; +sprinter_t *format = ctx-format; int i; -if (opt-offset 0) { - opt-offset += notmuch_query_count_messages (opt-query); - if (opt-offset 0) -
[PATCH v3 06/10] cli: Introduce notmuch address command
This moves address-related functionality from search command to the new address command. The implementation shares almost all code and some command line options. Options --offset and --limit were intentionally not included in the address command, because they refer to messages numbers, which users do not see in the output. This could confuse users because, for example, they could see more addresses in the output that what was specified with --limit. This functionality can be correctly reimplemented for address subcommand later. Also useless values of --exclude flag were not included in the address command. This was inspired by a patch from Jani Nikula. --- completion/notmuch-completion.bash | 42 - completion/notmuch-completion.zsh | 10 +++- doc/man1/notmuch-address.rst | 89 doc/man1/notmuch-search.rst| 20 +--- doc/man1/notmuch.rst | 7 +-- notmuch-client.h | 3 ++ notmuch-search.c | 93 +++--- notmuch.c | 2 + 8 files changed, 216 insertions(+), 50 deletions(-) create mode 100644 doc/man1/notmuch-address.rst diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cfbd389..94ea2d5 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) - COMPREPLY=( $( compgen -W summary threads messages files tags sender recipients -- ${cur} ) ) + COMPREPLY=( $( compgen -W summary threads messages files tags -- ${cur} ) ) return ;; --sort) @@ -320,6 +320,44 @@ _notmuch_search() esac } +_notmuch_address() +{ +local cur prev words cword split +_init_completion -s || return + +$split +case ${prev} in + --format) + COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) ) + return + ;; + --output) + COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) ) + return + ;; + --sort) + COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) ) + return + ;; + --exclude) + COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) ) + return + ;; +esac + +! $split +case ${cur} in + -*) + local options=--format= --output= --sort= --exclude= + compopt -o nospace + COMPREPLY=( $(compgen -W $options -- ${cur}) ) + ;; + *) + _notmuch_search_terms + ;; +esac +} + _notmuch_show() { local cur prev words cword split @@ -393,7 +431,7 @@ _notmuch_tag() _notmuch() { -local _notmuch_commands=compact config count dump help insert new reply restore search setup show tag +local _notmuch_commands=compact config count dump help insert new reply restore search address setup show tag local arg cur prev words cword split # require bash-completion with _init_completion diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 3e52a00..c606b75 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -10,6 +10,7 @@ _notmuch_commands() 'setup:interactively set up notmuch for first use' 'new:find and import any new message to the database' 'search:search for messages matching the search terms, display matching threads as results' +'address:get addresses from messages matching the given search terms' 'reply:constructs a reply template for a set of messages' 'show:show all messages matching the search terms' 'tag:add or remove tags for all messages matching the search terms' @@ -53,7 +54,14 @@ _notmuch_search() '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ '--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ -'--output=[select what to output]:output:((summary threads messages files tags sender recipients))' +'--output=[select what to output]:output:((summary threads messages files tags))' +} + +_notmuch_address() +{ + _arguments -s : \ +'--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ +'--output=[select what to output]:output:((sender recipients))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst new file mode 100644 index 000..d349237 --- /dev/null +++ b/doc/man1/notmuch-address.rst @@ -0,0 +1,89 @@ +=== +notmuch-address +=== + +SYNOPSIS + + +**notmuch** **address** [*option* ...]
[PATCH v6 6/7] cli: search: Add --output=count
Hi Tomi, On Mon, Nov 03 2014, Tomi Ollila wrote: > On Sun, Nov 02 2014, Michal Sojka wrote: > >> On 2. listopadu 2014 10:29:28 CET, Mark Walters > gmail.com> wrote: >>> >>>Hi >>> >>>On Sun, 02 Nov 2014, Michal Sojka wrote: >>>> On Sat, Nov 01 2014, Mark Walters wrote: >>>>> On Fri, 31 Oct 2014, Michal Sojka wrote: >>>>>> This output can be used with --output=recipients or --output=sender >>>>>> and in addition to the addresses, it prints how many times was each >>>>>> address encountered during search. >>>>> >>>>> Hi >>>>> >>>>> I have a couple comments on this patch. >>>>> >>>>>> --- >>>>>> completion/notmuch-completion.bash | 2 +- >>>>>> completion/notmuch-completion.zsh | 2 +- >>>>>> doc/man1/notmuch-search.rst| 9 +-- >>>>>> notmuch-search.c | 51 >>>-- >>>>>> 4 files changed, 52 insertions(+), 12 deletions(-) >>>>>> >>>>>> diff --git a/completion/notmuch-completion.bash >>>b/completion/notmuch-completion.bash >>>>>> index cfbd389..39cd829 100644 >>>>>> --- a/completion/notmuch-completion.bash >>>>>> +++ b/completion/notmuch-completion.bash >>>>>> @@ -294,7 +294,7 @@ _notmuch_search() >>>>>> return >>>>>> ;; >>>>>> --output) >>>>>> -COMPREPLY=( $( compgen -W "summary threads messages files >>>tags sender recipients" -- "${cur}" ) ) >>>>>> +COMPREPLY=( $( compgen -W "summary threads messages files >>>tags sender recipients count" -- "${cur}" ) ) >>>>>> return >>>>>> ;; >>>>>> --sort) >>>>>> diff --git a/completion/notmuch-completion.zsh >>>b/completion/notmuch-completion.zsh >>>>>> index 3e52a00..d7e5a5e 100644 >>>>>> --- a/completion/notmuch-completion.zsh >>>>>> +++ b/completion/notmuch-completion.zsh >>>>>> @@ -53,7 +53,7 @@ _notmuch_search() >>>>>> '--max-threads=[display only the first x threads from the >>>search results]:number of threads to show: ' \ >>>>>> '--first=[omit the first x threads from the search >>>results]:number of threads to omit: ' \ >>>>>> '--sort=[sort results]:sorting:((newest-first\:"reverse >>>chronological order" oldest-first\:"chronological order"))' \ >>>>>> -'--output=[select what to output]:output:((summary threads >>>messages files tags sender recipients))' >>>>>> +'--output=[select what to output]:output:((summary threads >>>messages files tags sender recipients count))' >>>>>> } >>>>>> >>>>>> _notmuch() >>>>>> diff --git a/doc/man1/notmuch-search.rst >>>b/doc/man1/notmuch-search.rst >>>>>> index 42f17e4..ec89200 100644 >>>>>> --- a/doc/man1/notmuch-search.rst >>>>>> +++ b/doc/man1/notmuch-search.rst >>>>>> @@ -96,9 +96,14 @@ Supported options for **search** include >>>>>> Like **sender** but for addresses from *To*, *Cc* and >>>>>> *Bcc* headers. >>>>>> >>>>>> +**count** >>>>>> +Can be used in combination with **sender** or >>>>>> +**recipients** to print the count of how many times was >>>>>> +the address encountered during search. >>>>>> + >>>>>> This option can be given multiple times to combine different >>>>>> -outputs. Currently, this is only supported for **sender** and >>>>>> -**recipients** outputs. >>>>>> +outputs. Currently, this is only supported for **sender**, >>>>>> +**recipients** and **count** outputs. >>>>> >>>>> It might be worth saying that the results will be slower if count is >>>>> specified. >>>> >>>> I wrote something like this in >>>> id:1414889400-30977-10-git-send-email-sojkam1 at fel.cvut.cz. >>>> >>>>> >>>>>> ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) >>>>> >>>>> I think sort works as expected if count is not specified, but does >>>not >>>>> with count. >>>> >>>> Agreed. >>>> >>>>> Maybe count can be done by doing two passes? >>>> >>>> What do you mean by pass? >>> >>>I mean iterating through the messages twice: so the first time would >>>fill in the count in the hash map, and the second iteration would print >>>the addresses. >> >> That's what the patch does. Only the second time it iterates the hash. > > Sorry > > And we loose the order ? (or not ?) do we have "orderedhash" there... > I'd like to save the order What would be the use case? If you really want to preserve the order, we could save a sequence number in mailbox_t and sort the hash table entries before printing. But this could be implemented later and enabled by a special command line option. notmuch search --output=tags does not preserve order either. > (but that is not so important) -- but more importantly, are tests > robusts --- ah there is sort -n in the test... Yes. -Michal
Re: [PATCH v6 6/7] cli: search: Add --output=count
Hi Tomi, On Mon, Nov 03 2014, Tomi Ollila wrote: On Sun, Nov 02 2014, Michal Sojka sojk...@fel.cvut.cz wrote: On 2. listopadu 2014 10:29:28 CET, Mark Walters markwalters1...@gmail.com wrote: Hi On Sun, 02 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote: On Sat, Nov 01 2014, Mark Walters wrote: On Fri, 31 Oct 2014, Michal Sojka sojk...@fel.cvut.cz wrote: This output can be used with --output=recipients or --output=sender and in addition to the addresses, it prints how many times was each address encountered during search. Hi I have a couple comments on this patch. --- completion/notmuch-completion.bash | 2 +- completion/notmuch-completion.zsh | 2 +- doc/man1/notmuch-search.rst| 9 +-- notmuch-search.c | 51 -- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cfbd389..39cd829 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) -COMPREPLY=( $( compgen -W summary threads messages files tags sender recipients -- ${cur} ) ) +COMPREPLY=( $( compgen -W summary threads messages files tags sender recipients count -- ${cur} ) ) return ;; --sort) diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 3e52a00..d7e5a5e 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -53,7 +53,7 @@ _notmuch_search() '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ '--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ -'--output=[select what to output]:output:((summary threads messages files tags sender recipients))' +'--output=[select what to output]:output:((summary threads messages files tags sender recipients count))' } _notmuch() diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst index 42f17e4..ec89200 100644 --- a/doc/man1/notmuch-search.rst +++ b/doc/man1/notmuch-search.rst @@ -96,9 +96,14 @@ Supported options for **search** include Like **sender** but for addresses from *To*, *Cc* and *Bcc* headers. +**count** +Can be used in combination with **sender** or +**recipients** to print the count of how many times was +the address encountered during search. + This option can be given multiple times to combine different -outputs. Currently, this is only supported for **sender** and -**recipients** outputs. +outputs. Currently, this is only supported for **sender**, +**recipients** and **count** outputs. It might be worth saying that the results will be slower if count is specified. I wrote something like this in id:1414889400-30977-10-git-send-email-sojk...@fel.cvut.cz. ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) I think sort works as expected if count is not specified, but does not with count. Agreed. Maybe count can be done by doing two passes? What do you mean by pass? I mean iterating through the messages twice: so the first time would fill in the count in the hash map, and the second iteration would print the addresses. That's what the patch does. Only the second time it iterates the hash. Sorry And we loose the order ? (or not ?) do we have orderedhash there... I'd like to save the order What would be the use case? If you really want to preserve the order, we could save a sequence number in mailbox_t and sort the hash table entries before printing. But this could be implemented later and enabled by a special command line option. notmuch search --output=tags does not preserve order either. (but that is not so important) -- but more importantly, are tests robusts --- ah there is sort -n in the test... Yes. -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 02/10] cli: search: Move more variables into search_context_t
Just refactoring, no functional changes. --- notmuch-search.c | 49 ++--- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 2c47b80..3d2012b 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -34,8 +34,18 @@ typedef enum { #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS) +typedef enum { +NOTMUCH_FORMAT_JSON, +NOTMUCH_FORMAT_TEXT, +NOTMUCH_FORMAT_TEXT0, +NOTMUCH_FORMAT_SEXP +} format_sel_t; + typedef struct { +notmuch_database_t *notmuch; +format_sel_t format_sel; sprinter_t *format; +notmuch_exclude_t exclude; notmuch_query_t *query; notmuch_sort_t sort; output_t output; @@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx) } static int -do_search_tags (notmuch_database_t *notmuch, - const search_context_t *ctx) +do_search_tags (const search_context_t *ctx) { notmuch_messages_t *messages = NULL; notmuch_tags_t *tags; const char *tag; sprinter_t *format = ctx-format; notmuch_query_t *query = ctx-query; +notmuch_database_t *notmuch = ctx-notmuch; /* should the following only special case if no excluded terms * specified? */ @@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch, int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { -notmuch_database_t *notmuch; search_context_t ctx = { + .format_sel = NOTMUCH_FORMAT_TEXT, + .exclude = NOTMUCH_EXCLUDE_TRUE, .sort = NOTMUCH_SORT_NEWEST_FIRST, .output = 0, .offset = 0, @@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) }; char *query_str; int opt_index, ret; -notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE; unsigned int i; -enum { - NOTMUCH_FORMAT_JSON, - NOTMUCH_FORMAT_TEXT, - NOTMUCH_FORMAT_TEXT0, - NOTMUCH_FORMAT_SEXP -} format_sel = NOTMUCH_FORMAT_TEXT; - notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, ctx.sort, sort, 's', (notmuch_keyword_t []){ { oldest-first, NOTMUCH_SORT_OLDEST_FIRST }, { newest-first, NOTMUCH_SORT_NEWEST_FIRST }, { 0, 0 } } }, - { NOTMUCH_OPT_KEYWORD, format_sel, format, 'f', + { NOTMUCH_OPT_KEYWORD, ctx.format_sel, format, 'f', (notmuch_keyword_t []){ { json, NOTMUCH_FORMAT_JSON }, { sexp, NOTMUCH_FORMAT_SEXP }, { text, NOTMUCH_FORMAT_TEXT }, @@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { files, OUTPUT_FILES }, { tags, OUTPUT_TAGS }, { 0, 0 } } }, -{ NOTMUCH_OPT_KEYWORD, exclude, exclude, 'x', +{ NOTMUCH_OPT_KEYWORD, ctx.exclude, exclude, 'x', (notmuch_keyword_t []){ { true, NOTMUCH_EXCLUDE_TRUE }, { false, NOTMUCH_EXCLUDE_FALSE }, { flag, NOTMUCH_EXCLUDE_FLAG }, @@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (format_sel) { +switch (ctx.format_sel) { case NOTMUCH_FORMAT_TEXT: ctx.format = sprinter_text_create (config, stdout); break; @@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_exit_if_unsupported_format (); if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, notmuch)) + NOTMUCH_DATABASE_MODE_READ_ONLY, ctx.notmuch)) return EXIT_FAILURE; -query_str = query_string_from_args (notmuch, argc-opt_index, argv+opt_index); +query_str = query_string_from_args (ctx.notmuch, argc-opt_index, argv+opt_index); if (query_str == NULL) { fprintf (stderr, Out of memory.\n); return EXIT_FAILURE; @@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -ctx.query = notmuch_query_create (notmuch, query_str); +ctx.query = notmuch_query_create (ctx.notmuch, query_str); if (ctx.query == NULL) { fprintf (stderr, Out of memory\n); return EXIT_FAILURE; @@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_query_set_sort (ctx.query, ctx.sort); -if (exclude == NOTMUCH_EXCLUDE_FLAG ctx.output != OUTPUT_SUMMARY) { +if (ctx.exclude == NOTMUCH_EXCLUDE_FLAG ctx.output != OUTPUT_SUMMARY) { /* If we are not doing summary output there is nowhere to * print the excluded flag so fall
[PATCH v2 06/10] cli: Introduce notmuch address command
This moves address-related functionality from search command to the new address command. The implementation shares almost all code and some command line options. Options --offset and --limit were intentionally not included in the address command, because they refer to messages numbers, which users do not see in the output. This could confuse users because, for example, they could see more addresses in the output that what was specified with --limit. This functionality can be correctly reimplemented for addresses later. This was inspired by a patch from Jani Nikula. --- completion/notmuch-completion.bash | 42 ++- completion/notmuch-completion.zsh | 10 +++- doc/man1/notmuch-address.rst | 99 doc/man1/notmuch-search.rst| 20 +--- doc/man1/notmuch.rst | 7 +-- notmuch-client.h | 3 ++ notmuch-search.c | 101 + notmuch.c | 2 + 8 files changed, 228 insertions(+), 56 deletions(-) create mode 100644 doc/man1/notmuch-address.rst diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cfbd389..94ea2d5 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) - COMPREPLY=( $( compgen -W summary threads messages files tags sender recipients -- ${cur} ) ) + COMPREPLY=( $( compgen -W summary threads messages files tags -- ${cur} ) ) return ;; --sort) @@ -320,6 +320,44 @@ _notmuch_search() esac } +_notmuch_address() +{ +local cur prev words cword split +_init_completion -s || return + +$split +case ${prev} in + --format) + COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) ) + return + ;; + --output) + COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) ) + return + ;; + --sort) + COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) ) + return + ;; + --exclude) + COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) ) + return + ;; +esac + +! $split +case ${cur} in + -*) + local options=--format= --output= --sort= --exclude= + compopt -o nospace + COMPREPLY=( $(compgen -W $options -- ${cur}) ) + ;; + *) + _notmuch_search_terms + ;; +esac +} + _notmuch_show() { local cur prev words cword split @@ -393,7 +431,7 @@ _notmuch_tag() _notmuch() { -local _notmuch_commands=compact config count dump help insert new reply restore search setup show tag +local _notmuch_commands=compact config count dump help insert new reply restore search address setup show tag local arg cur prev words cword split # require bash-completion with _init_completion diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 3e52a00..c606b75 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -10,6 +10,7 @@ _notmuch_commands() 'setup:interactively set up notmuch for first use' 'new:find and import any new message to the database' 'search:search for messages matching the search terms, display matching threads as results' +'address:get addresses from messages matching the given search terms' 'reply:constructs a reply template for a set of messages' 'show:show all messages matching the search terms' 'tag:add or remove tags for all messages matching the search terms' @@ -53,7 +54,14 @@ _notmuch_search() '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ '--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ -'--output=[select what to output]:output:((summary threads messages files tags sender recipients))' +'--output=[select what to output]:output:((summary threads messages files tags))' +} + +_notmuch_address() +{ + _arguments -s : \ +'--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ +'--output=[select what to output]:output:((sender recipients))' } _notmuch() diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst new file mode 100644 index 000..8109f11 --- /dev/null +++ b/doc/man1/notmuch-address.rst @@ -0,0 +1,99 @@ +=== +notmuch-address +=== + +SYNOPSIS + + +**notmuch** **address** [*option* ...] *search-term* ... + +DESCRIPTION +=== + +Search for messages matching the given
[PATCH v2 04/10] cli: search: Split notmuch_search_command to smaller functions
In the next commit, these functions will be used to share some functionality between search and address commands. --- notmuch-search.c | 155 ++- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 6765a16..f115359 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx) return 0; } +static int +_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int argc, char *argv[]) +{ +char *query_str; +unsigned int i; + +switch (ctx-format_sel) { +case NOTMUCH_FORMAT_TEXT: + ctx-format = sprinter_text_create (config, stdout); + break; +case NOTMUCH_FORMAT_TEXT0: + if (ctx-output == OUTPUT_SUMMARY) { + fprintf (stderr, Error: --format=text0 is not compatible with --output=summary.\n); + return EXIT_FAILURE; + } + ctx-format = sprinter_text0_create (config, stdout); + break; +case NOTMUCH_FORMAT_JSON: + ctx-format = sprinter_json_create (config, stdout); + break; +case NOTMUCH_FORMAT_SEXP: + ctx-format = sprinter_sexp_create (config, stdout); + break; +default: + /* this should never happen */ + INTERNAL_ERROR(no output format selected); +} + +notmuch_exit_if_unsupported_format (); + +if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch)) + return EXIT_FAILURE; + +query_str = query_string_from_args (ctx-notmuch, argc, argv); +if (query_str == NULL) { + fprintf (stderr, Out of memory.\n); + return EXIT_FAILURE; +} +if (*query_str == '\0') { + fprintf (stderr, Error: notmuch search requires at least one search term.\n); + return EXIT_FAILURE; +} + +ctx-query = notmuch_query_create (ctx-notmuch, query_str); +if (ctx-query == NULL) { + fprintf (stderr, Out of memory\n); + return EXIT_FAILURE; +} + +notmuch_query_set_sort (ctx-query, ctx-sort); + +if (ctx-exclude == NOTMUCH_EXCLUDE_FLAG ctx-output != OUTPUT_SUMMARY) { + /* If we are not doing summary output there is nowhere to +* print the excluded flag so fall back on including the +* excluded messages. */ + fprintf (stderr, Warning: this output format cannot flag excluded messages.\n); + ctx-exclude = NOTMUCH_EXCLUDE_FALSE; +} + +if (ctx-exclude != NOTMUCH_EXCLUDE_FALSE) { + const char **search_exclude_tags; + size_t search_exclude_tags_length; + + search_exclude_tags = notmuch_config_get_search_exclude_tags + (config, search_exclude_tags_length); + for (i = 0; i search_exclude_tags_length; i++) + notmuch_query_add_tag_exclude (ctx-query, search_exclude_tags[i]); + notmuch_query_set_omit_excluded (ctx-query, ctx-exclude); +} + +return 0; +} + +static void +_notmuch_search_cleanup (search_context_t *ctx) +{ +notmuch_query_destroy (ctx-query); +notmuch_database_destroy (ctx-notmuch); + +talloc_free (ctx-format); +} + int notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { @@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) .dupe = -1, }; search_context_t *ctx = search_context; -char *query_str; int opt_index, ret; -unsigned int i; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, ctx-sort, sort, 's', @@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } -switch (ctx-format_sel) { -case NOTMUCH_FORMAT_TEXT: - ctx-format = sprinter_text_create (config, stdout); - break; -case NOTMUCH_FORMAT_TEXT0: - if (ctx-output == OUTPUT_SUMMARY) { - fprintf (stderr, Error: --format=text0 is not compatible with --output=summary.\n); - return EXIT_FAILURE; - } - ctx-format = sprinter_text0_create (config, stdout); - break; -case NOTMUCH_FORMAT_JSON: - ctx-format = sprinter_json_create (config, stdout); - break; -case NOTMUCH_FORMAT_SEXP: - ctx-format = sprinter_sexp_create (config, stdout); - break; -default: - /* this should never happen */ - INTERNAL_ERROR(no output format selected); -} - -notmuch_exit_if_unsupported_format (); - -if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch)) +if (_notmuch_search_prepare (ctx, config, +argc - opt_index, argv + opt_index)) return EXIT_FAILURE; -query_str = query_string_from_args (ctx-notmuch, argc-opt_index, argv+opt_index); -if (query_str == NULL) { -