--- /dev/null
+Return-Path: <markwalters1009@gmail.com>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by arlo.cworth.org (Postfix) with ESMTP id 77F8C6DE02B0\r
+ for <notmuch@notmuchmail.org>; Fri, 20 May 2016 13:13:31 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at cworth.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.317\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.317 tagged_above=-999 required=5 tests=[AWL=0.253,\r
+ DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
+ FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7,\r
+ RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001]\r
+ autolearn=disabled\r
+Received: from arlo.cworth.org ([127.0.0.1])\r
+ by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id dLadyyo6R54u for <notmuch@notmuchmail.org>;\r
+ Fri, 20 May 2016 13:13:23 -0700 (PDT)\r
+Received: from mail-wm0-f50.google.com (mail-wm0-f50.google.com\r
+ [74.125.82.50]) by arlo.cworth.org (Postfix) with ESMTPS id 0A9CF6DE01D0 for\r
+ <notmuch@notmuchmail.org>; Fri, 20 May 2016 13:13:23 -0700 (PDT)\r
+Received: by mail-wm0-f50.google.com with SMTP id v200so2820578wmv.0\r
+ for <notmuch@notmuchmail.org>; Fri, 20 May 2016 13:13:22 -0700 (PDT)\r
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;\r
+ h=from:to:cc:subject:date:message-id:in-reply-to:references;\r
+ bh=nC40kPmLpCoVYninYfZb/9BJ9qzorpGmvAD8wxo1Xag=;\r
+ b=zvJXgVew/QHyC8m1brYMNY4F7qdfT0M3sP4VoRjNWoZTyj328WRdVcc4N7He7Z/7ld\r
+ qwa/tj4L9hMu3eeyK+7EHKZvxE5rEdxpHfkT8SMZNr75+LyCduV/ilvAzQ76bhUwfB89\r
+ hUTiUIRFn6S20bV75TFYWiyY3uBjrg+fYs3Ab2Skw2TsPn9jRui5f/WYPjj5iYIvrCxX\r
+ DDu6UcY0p8nwp5BXqv35gcDPIVPdlEUmL5vEll0MhGR5tLmOwWaOB+d8qT7XQenkUgmk\r
+ WlnYGz+hP5WkLH12tJ1M6d+dIbqq6iaJ6YOjPK9wj5zlma+ffSznbIFprP998xM84doG\r
+ JOyg==\r
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
+ d=1e100.net; s=20130820;\r
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\r
+ :references;\r
+ bh=nC40kPmLpCoVYninYfZb/9BJ9qzorpGmvAD8wxo1Xag=;\r
+ b=fQI7uXWUWXHV92+pU7mBBXoyDqdmZITlpwU+VrkC4muYPHWSUkGO33RlGEi3561JHW\r
+ P3pXtVtePEJa3MKe6tZb9QgIXShczDocLinb7pYHWZaJMerE7SqcOjCjdSMyUtl8zXMx\r
+ ksGzxJU8wGJJdvgzXM5sRLM7iRBvDjBKEmPPgTF4hdGPSnn2IWncs2+9r8dp11p8p6h7\r
+ h4/BaxtXOmPMOanT7D29UUsCOxRn5CaY+V0KQO7POBDLUbtjzG2SwuaGAuTMPa7AJojC\r
+ qsZsz3bTTdVwr4fVUELw27YSazgVTMqaSBtSEiDLei1OKefix+r3xN3h1PjoaxhH4Euv\r
+ LFBw==\r
+X-Gm-Message-State:\r
+ AOPr4FVL1F8S2lVjE8bh9FDXopjV1v2AtvZ7zkEe3jnukdSVeMmRL+UG74eXIi4+ySpMyA==\r
+X-Received: by 10.194.234.71 with SMTP id uc7mr5691932wjc.80.1463775201517;\r
+ Fri, 20 May 2016 13:13:21 -0700 (PDT)\r
+Received: from localhost (5751dfa2.skybroadband.com. [87.81.223.162])\r
+ by smtp.gmail.com with ESMTPSA id h4sm21374963wjz.20.2016.05.20.13.13.16\r
+ (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\r
+ Fri, 20 May 2016 13:13:17 -0700 (PDT)\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 1/2] emacs: address completion,\r
+ allow sender/recipient and filters\r
+Date: Fri, 20 May 2016 21:13:02 +0100\r
+Message-Id: <1463775183-16234-2-git-send-email-markwalters1009@gmail.com>\r
+X-Mailer: git-send-email 2.1.4\r
+In-Reply-To: <1463775183-16234-1-git-send-email-markwalters1009@gmail.com>\r
+References: <1463775183-16234-1-git-send-email-markwalters1009@gmail.com>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.20\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch/>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 20 May 2016 20:13:31 -0000\r
+\r
+This commit lets the user customize the address completion. It makes\r
+two changes.\r
+\r
+The first change controls whether to build the address completion list\r
+based on messages you have sent or you have received (the latter is\r
+much faster).\r
+\r
+The second change add a possible filter query to limit the messages\r
+used -- for example, setting this to date:1y.. would limit the\r
+address completions to addresses used in the last year. This speeds up\r
+the address harvest and may also make the search less cluttered as old\r
+addresses may well no longer be valid.\r
+---\r
+ emacs/notmuch-address.el | 135 +++++++++++++++++++++++++++++++++--------------\r
+ emacs/notmuch-company.el | 2 +-\r
+ emacs/notmuch-mua.el | 3 +-\r
+ 3 files changed, 98 insertions(+), 42 deletions(-)\r
+\r
+diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el\r
+index aafbe5f..3e7bdab 100644\r
+--- a/emacs/notmuch-address.el\r
++++ b/emacs/notmuch-address.el\r
+@@ -28,15 +28,62 @@\r
+ ;;\r
+ (declare-function company-manual-begin "company")\r
+ \r
++(defvar notmuch-address-last-harvest 0\r
++ "Time of last address harvest")\r
++\r
++(defvar notmuch-address-completions (make-hash-table :test 'equal)\r
++ "Hash of email addresses for completion during email composition.\r
++ This variable is set by calling `notmuch-address-harvest'.")\r
++\r
++(defvar notmuch-address-full-harvest-finished nil\r
++ "t indicates that full completion address harvesting has been\r
++finished")\r
++\r
+ (defcustom notmuch-address-command 'internal\r
+- "The command which generates possible addresses. It must take a\r
+-single argument and output a list of possible matches, one per\r
+-line. The default value of `internal' uses built-in address\r
+-completion."\r
++ "Determines how address completion candidates are generated.\r
++\r
++If it is a string then that string should be an external program\r
++which must take a single argument (searched string) and output a\r
++list of completion candidates, one per line.\r
++\r
++Alternatively, it can be the symbol 'internal, in which case\r
++internal completion is used; the variable\r
++`notmuch-address-internal-completion` can be used to customize\r
++this case.\r
++\r
++Finally, if this variable is nil then address completion is\r
++disabled."\r
+ :type '(radio\r
+ (const :tag "Use internal address completion" internal)\r
+ (const :tag "Disable address completion" nil)\r
+- (string :tag "Use external completion command" "notmuch-addresses"))\r
++ (string :tag "Use external completion command"))\r
++ :group 'notmuch-send\r
++ :group 'notmuch-external)\r
++\r
++(defcustom notmuch-address-internal-completion '(sent nil)\r
++ "Determines how internal address completion generates candidates.\r
++\r
++This should be a list of the form '(DIRECTION FILTER), where\r
++ DIRECTION is either sent or received and specifies whether the\r
++ candidates are searched in messages sent by the user or received\r
++ by the user (note received by is much faster), and FILTER is\r
++ either nil or a filter-string, such as \"date:1y..\" to append\r
++ to the query."\r
++ :type '(list :tag "Use internal address completion"\r
++ (radio\r
++ :tag "Base completion on messages you have"\r
++ :value sent\r
++ (const :tag "sent (more accurate)" sent)\r
++ (const :tag "received (faster)" received))\r
++ (radio :tag "Filter messages used for completion"\r
++ (const :tag "Use all messages" nil)\r
++ (string :tag "Filter query")))\r
++ ;; We override set so that we can clear the cache when this changes\r
++ :set (lambda (symbol value)\r
++ (set-default symbol value)\r
++ (setq notmuch-address-last-harvest 0)\r
++ (setq notmuch-address-completions (clrhash notmuch-address-completions))\r
++ (setq notmuch-address-full-harvest-finished nil))\r
+ :group 'notmuch-send\r
+ :group 'notmuch-external)\r
+ \r
+@@ -51,17 +98,6 @@ to know how address selection is made by default."\r
+ :group 'notmuch-send\r
+ :group 'notmuch-external)\r
+ \r
+-(defvar notmuch-address-last-harvest 0\r
+- "Time of last address harvest")\r
+-\r
+-(defvar notmuch-address-completions (make-hash-table :test 'equal)\r
+- "Hash of email addresses for completion during email composition.\r
+- This variable is set by calling `notmuch-address-harvest'.")\r
+-\r
+-(defvar notmuch-address-full-harvest-finished nil\r
+- "t indicates that full completion address harvesting has been\r
+-finished")\r
+-\r
+ (defun notmuch-address-selection-function (prompt collection initial-input)\r
+ "Call (`completing-read'\r
+ PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)"\r
+@@ -86,9 +122,7 @@ finished")\r
+ (eq notmuch-address-command 'internal)\r
+ (require 'company nil t)))\r
+ (pair (cons notmuch-address-completion-headers-regexp\r
+- (if use-company\r
+- #'company-manual-begin\r
+- #'notmuch-address-expand-name))))\r
++ #'notmuch-address-expand-name)))\r
+ (when use-company\r
+ (notmuch-company-setup))\r
+ (unless (memq pair message-completion-alist)\r
+@@ -115,7 +149,7 @@ external commands."\r
+ (when (not notmuch-address-full-harvest-finished)\r
+ ;; First, run quick synchronous harvest based on what the user\r
+ ;; entered so far\r
+- (notmuch-address-harvest (format "to:%s*" original) t))\r
++ (notmuch-address-harvest original t))\r
+ (prog1 (notmuch-address-matching original)\r
+ ;; Then start the (potentially long-running) full asynchronous harvest if necessary\r
+ (notmuch-address-harvest-trigger)))\r
+@@ -123,7 +157,12 @@ external commands."\r
+ (process-lines notmuch-address-command original))))\r
+ \r
+ (defun notmuch-address-expand-name ()\r
+- (when notmuch-address-command\r
++ (cond\r
++ ((and (eq notmuch-address-command 'internal)\r
++ notmuch-address-use-company\r
++ (bound-and-true-p company-mode))\r
++ (company-manual-begin))\r
++ (notmuch-address-command\r
+ (let* ((end (point))\r
+ (beg (save-excursion\r
+ (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")\r
+@@ -149,7 +188,8 @@ external commands."\r
+ (delete-region beg end)\r
+ (insert chosen))\r
+ (message "No matches.")\r
+- (ding)))))\r
++ (ding))))\r
++ (t nil)))\r
+ \r
+ ;; Copied from `w3m-which-command'.\r
+ (defun notmuch-address-locate-command (command)\r
+@@ -191,32 +231,49 @@ external commands."\r
+ \r
+ The car is a partial harvest, and the cdr is a full harvest")\r
+ \r
+-(defun notmuch-address-harvest (&optional filter-query synchronous callback)\r
+- "Collect addresses completion candidates. It queries the\r
+-notmuch database for all messages sent by the user optionally\r
+-matching FILTER-QUERY (if not nil). It collects the destination\r
+-addresses from those messages and stores them in\r
+-`notmuch-address-completions'. Address harvesting may take some\r
+-time so the address collection runs asynchronously unless\r
+-SYNCHRONOUS is t. In case of asynchronous execution, CALLBACK is\r
+-called when harvesting finishes."\r
+- (let* ((from-me-query (mapconcat (lambda (x) (concat "from:" x)) (notmuch-user-emails) " or "))\r
+- (query (if filter-query\r
+- (format "(%s) and (%s)" from-me-query filter-query)\r
+- from-me-query))\r
++(defun notmuch-address-harvest (&optional addr-prefix synchronous callback)\r
++ "Collect addresses completion candidates.\r
++\r
++It queries the notmuch database for messages sent/received (as\r
++configured with `notmuch-address-command`) by the user, collects\r
++destination/source addresses from those messages and stores them\r
++in `notmuch-address-completions'.\r
++\r
++If ADDR-PREFIX is not nil, only messages with to/from addresses\r
++matching ADDR-PREFIX*' are queried.\r
++\r
++Address harvesting may take some time so the address collection runs\r
++asynchronously unless SYNCHRONOUS is t. In case of asynchronous\r
++execution, CALLBACK is called when harvesting finishes."\r
++\r
++ (let* ((sent (eq (car notmuch-address-internal-completion) 'sent))\r
++ (config-query (cadr notmuch-address-internal-completion))\r
++ (prefix-query (when addr-prefix\r
++ (format "%s:%s*" (if sent "to" "from") addr-prefix)))\r
++ (from-or-to-me-query\r
++ (mapconcat (lambda (x)\r
++ (concat (if sent "from:" "to:") x))\r
++ (notmuch-user-emails) " or "))\r
++ (query (if (or prefix-query config-query)\r
++ (concat (format "(%s)" from-or-to-me-query)\r
++ (when prefix-query\r
++ (format " and (%s)" prefix-query))\r
++ (when config-query\r
++ (format " and (%s)" config-query)))\r
++ from-or-to-me-query))\r
+ (args `("address" "--format=sexp" "--format-version=2"\r
+- "--output=recipients"\r
++ ,(if sent "--output=recipients" "--output=sender")\r
+ "--deduplicate=address"\r
+ ,query)))\r
+ (if synchronous\r
+ (mapc #'notmuch-address-harvest-addr\r
+ (apply 'notmuch-call-notmuch-sexp args))\r
+ ;; Asynchronous\r
+- (let* ((current-proc (if filter-query\r
++ (let* ((current-proc (if addr-prefix\r
+ (car notmuch-address-harvest-procs)\r
+ (cdr notmuch-address-harvest-procs)))\r
+ (proc-name (format "notmuch-address-%s-harvest"\r
+- (if filter-query "partial" "full")))\r
++ (if addr-prefix "partial" "full")))\r
+ (proc-buf (concat " *" proc-name "*")))\r
+ ;; Kill any existing process\r
+ (when current-proc\r
+@@ -228,7 +285,7 @@ called when harvesting finishes."\r
+ args))\r
+ (set-process-filter current-proc 'notmuch-address-harvest-filter)\r
+ (set-process-query-on-exit-flag current-proc nil)\r
+- (if filter-query\r
++ (if addr-prefix\r
+ (setcar notmuch-address-harvest-procs current-proc)\r
+ (setcdr notmuch-address-harvest-procs current-proc)))))\r
+ ;; return value\r
+diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el\r
+index b881d6d..dcb59cd 100644\r
+--- a/emacs/notmuch-company.el\r
++++ b/emacs/notmuch-company.el\r
+@@ -72,7 +72,7 @@\r
+ (lambda (callback)\r
+ ;; First run quick asynchronous harvest based on what the user entered so far\r
+ (notmuch-address-harvest\r
+- (format "to:%s*" arg) nil\r
++ arg nil\r
+ (lambda (_proc _event)\r
+ (funcall callback (notmuch-address-matching arg))\r
+ ;; Then start the (potentially long-running) full asynchronous harvest if necessary\r
+diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el\r
+index 399e138..93a4d4b 100644\r
+--- a/emacs/notmuch-mua.el\r
++++ b/emacs/notmuch-mua.el\r
+@@ -276,8 +276,7 @@ mutiple parts get a header."\r
+ \r
+ (define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]"\r
+ "Notmuch message composition mode. Mostly like `message-mode'"\r
+- (when notmuch-address-command\r
+- (notmuch-address-setup)))\r
++ (notmuch-address-setup))\r
+ \r
+ (put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)\r
+ \r
+-- \r
+2.1.4\r
+\r