[PATCH] emacs: allow to set RETAIN-STATE for `notmuch-show-refresh-view' interactively

2012-02-14 Thread David Bremner
On Mon, 13 Feb 2012 15:09:07 +0400, Dmitry Kurochkin  wrote:
> The notmuch-show view refresh function (`notmuch-show-refresh-view',
> bound to "=") accepts an optional RETAIN-STATE argument.  The patch
> allows to set this argument interactively by using "C-u =".

pushed

d


[PATCH] Free the results of scandir()

2012-02-14 Thread David Bremner
On Tue,  7 Feb 2012 05:05:03 -0500, Ethan Glasser-Camp  
wrote:
> From: Ethan Glasser-Camp 
> 
> scandir() returns "strings allocated via malloc(3)" which are then
> "collected in array namelist which is allocated via
> malloc(3)". Currently we just free the array namelist. Instead, free
> all the entries of namelist, and then free namelist.

pushed, thanks

d


[PATCH 0/8] Rewrite JSON show format

2012-02-14 Thread Adam Wolfe Gordon
Hi Austin,

On Tue, Feb 14, 2012 at 10:33, Austin Clements  wrote:
> The saga continues. ?As for the text format, this first shifts lots of
> code around without changing its semantics, then it dives in and
> simplifies a lot of things. ?Don't be put off by the number of
> patches; most of them are straightforward.

I haven't looked thoroughly style-wise, but otherwise everything LGTM.
I really like the new formatters - their operation is much easier to
follow than the old ones.

> As an added bonus, I documented (!) the JSON format for both show and
> search.

Good idea. I'll add my JSON reply format to this once it seems final.


[PATCH v5 0/4] Reply enhancements (was: quoting HTML email ...)

2012-02-14 Thread Adam Wolfe Gordon
I've just sent new versions of parts 1, 2, and 4. The only change is
that I've changed the output of the reply headers to be consistent
with the other JSON formats (capitalized, in the right order). Of
course, that had a ripple effect on the tests and emacs.

Sorry for the double-mail - I just realized I should change these
things while reviewing Austin's patches.


[PATCH v5.1 4/4] emacs: Use the new JSON reply format and message-cite-original

2012-02-14 Thread Adam Wolfe Gordon
Using the new JSON reply format allows emacs to quote HTML parts
nicely by using mm-display-part to turn them into displayable text,
then quoting them with message-cite-original. This is very useful for
users who regularly receive HTML-only email.

Use message-mode's message-cite-original function to create the
quoted body for reply messages. In order to make this act like the
existing notmuch defaults, you will need to set the following in
your emacs configuration:

message-citation-line-format "On %a, %d %b %Y, %f wrote:"
message-citation-line-function 'message-insert-formatted-citation-line

The test has been updated to reflect the (ugly) emacs default.
---

Adjusted the header display to be consistent with the other JSON formats.

 emacs/notmuch-lib.el  |   39 +++
 emacs/notmuch-mua.el  |  123 +++--
 emacs/notmuch-show.el |   24 +-
 test/emacs|  101 +++-
 4 files changed, 228 insertions(+), 59 deletions(-)

diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
index d315f76..3fc7aff 100644
--- a/emacs/notmuch-lib.el
+++ b/emacs/notmuch-lib.el
@@ -21,6 +21,8 @@

 ;; This is an part of an emacs-based interface to the notmuch mail system.

+(eval-when-compile (require 'cl))
+
 (defvar notmuch-command "notmuch"
   "Command to run the notmuch binary.")

@@ -173,6 +175,43 @@ the user hasn't set this variable with the old or new 
value."
   (list 'when (< emacs-major-version 23)
form))

+(defun notmuch-split-content-type (content-type)
+  "Split content/type into 'content' and 'type'"
+  (split-string content-type "/"))
+
+(defun notmuch-match-content-type (t1 t2)
+  "Return t if t1 and t2 are matching content types, taking wildcards into 
account"
+  (let ((st1 (notmuch-split-content-type t1))
+   (st2 (notmuch-split-content-type t2)))
+(if (or (string= (cadr st1) "*")
+   (string= (cadr st2) "*"))
+   (string= (car st1) (car st2))
+  (string= t1 t2
+
+(defvar notmuch-multipart/alternative-discouraged
+  '(
+;; Avoid HTML parts.
+"text/html"
+;; multipart/related usually contain a text/html part and some associated 
graphics.
+"multipart/related"
+))
+
+(defun notmuch-multipart/alternative-choose (types)
+  "Return a list of preferred types from the given list of types"
+  ;; Based on `mm-preferred-alternative-precedence'.
+  (let ((seq types))
+(dolist (pref (reverse notmuch-multipart/alternative-discouraged))
+  (dolist (elem (copy-sequence seq))
+   (when (string-match pref elem)
+ (setq seq (nconc (delete elem seq) (list elem))
+seq))
+
+(defun notmuch-parts-filter-by-type (parts type)
+  "Given a vector of message parts, return a vector containing the ones 
matching the given type."
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) type)
+   vconcat (list part)))
+
 ;; Compatibility functions for versions of emacs before emacs 23.
 ;;
 ;; Both functions here were copied from emacs 23 with the following copyright:
diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 4be7c13..c2fc8c5 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -19,11 +19,15 @@
 ;;
 ;; Authors: David Edmondson 

+(require 'json)
 (require 'message)
+(require 'format-spec)

 (require 'notmuch-lib)
 (require 'notmuch-address)

+(eval-when-compile (require 'cl))
+
 ;;

 (defcustom notmuch-mua-send-hook '(notmuch-mua-message-send-hook)
@@ -72,56 +76,105 @@ list."
(push header message-hidden-headers)))
notmuch-mua-hidden-headers))

+(defun notmuch-mua-get-displayed-part (part query-string)
+  (with-temp-buffer
+(if (assq 'content part)
+   (insert (cdr (assq 'content part)))
+  (call-process notmuch-command nil t nil "show" "--format=raw"
+   (format "--part=%s" (cdr (assq 'id part)))
+   query-string))
+
+(let ((handle (mm-make-handle (current-buffer) (list (cdr (assq 
'content-type part)
+ (end-of-orig (point-max)))
+  (mm-display-part handle)
+  (delete-region (point-min) end-of-orig)
+  (buffer-substring (point-min) (point-max)
+
+(defun notmuch-mua-multipart/*-to-list (parts)
+  (loop for part across parts
+   collect (cdr (assq 'content-type part
+
+(defun notmuch-mua-get-quotable-parts (parts)
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) 
"multipart/alternative")
+ append (let* ((subparts (cdr (assq 'content part)))
+   (types (notmuch-mua-multipart/*-to-list subparts))
+   (chosen-type (car (notmuch-multipart/alternative-choose 
types
+  (notmuch-mua-get-quotable-parts 
(notmuch-parts-filter-by-type subparts chosen-type)))
+   else if (notmuch-match-content-type (cdr (assq 'content-type part)) 
"multipart/*")
+ append 

[PATCH v5.1 2/4] reply: Add a JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
This new JSON format for replies includes headers generated for a
reply message as well as the headers of the original message.  Using
this data, a client can intelligently create a reply. For example, the
emacs client will be able to create replies with quoted HTML parts by
parsing the HTML parts.

Reply now enforces that only one message is returned, as the semantics
of replying to multiple messages are not well-defined.
---

Adjusted the header display to be consistent with the other JSON formats.

 notmuch-client.h |3 +
 notmuch-reply.c  |  188 +
 notmuch-show.c   |2 +-
 3 files changed, 149 insertions(+), 44 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 60828aa..d28ea07 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -344,6 +344,9 @@ typedef struct mime_node {
 int next_part_num;
 } mime_node_t;

+void
+format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);
+
 /* Construct a new MIME node pointing to the root message part of
  * message.  If cryptoctx is non-NULL, it will be used to verify
  * signatures on any child parts.  If decrypt is true, then cryptoctx
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 6b244e6..76995b1 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -505,6 +505,61 @@ guess_from_received_header (notmuch_config_t *config, 
notmuch_message_t *message
 return NULL;
 }

+static GMimeMessage *
+create_reply_message(void *ctx,
+notmuch_config_t *config,
+notmuch_message_t *message,
+notmuch_bool_t reply_all)
+{
+const char *subject, *from_addr = NULL;
+const char *in_reply_to, *orig_references, *references;
+
+/* The 1 means we want headers in a "pretty" order. */
+GMimeMessage *reply = g_mime_message_new (1);
+if (reply == NULL) {
+   fprintf (stderr, "Out of memory\n");
+   return NULL;
+}
+
+subject = notmuch_message_get_header (message, "subject");
+if (subject) {
+   if (strncasecmp (subject, "Re:", 3))
+   subject = talloc_asprintf (ctx, "Re: %s", subject);
+   g_mime_message_set_subject (reply, subject);
+}
+
+from_addr = add_recipients_from_message (reply, config,
+message, reply_all);
+
+if (from_addr == NULL)
+   from_addr = guess_from_received_header (config, message);
+
+if (from_addr == NULL)
+   from_addr = notmuch_config_get_user_primary_email (config);
+
+from_addr = talloc_asprintf (ctx, "%s <%s>",
+notmuch_config_get_user_name (config),
+from_addr);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ "From", from_addr);
+
+in_reply_to = talloc_asprintf (ctx, "<%s>",
+  notmuch_message_get_message_id (message));
+
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ "In-Reply-To", in_reply_to);
+
+orig_references = notmuch_message_get_header (message, "references");
+references = talloc_asprintf (ctx, "%s%s%s",
+ orig_references ? orig_references : "",
+ orig_references ? " " : "",
+ in_reply_to);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ "References", references);
+
+return reply;
+}
+
 static int
 notmuch_reply_format_default(void *ctx,
 notmuch_config_t *config,
@@ -515,8 +570,6 @@ notmuch_reply_format_default(void *ctx,
 GMimeMessage *reply;
 notmuch_messages_t *messages;
 notmuch_message_t *message;
-const char *subject, *from_addr = NULL;
-const char *in_reply_to, *orig_references, *references;
 const notmuch_show_format_t *format = _reply;

 for (messages = notmuch_query_search_messages (query);
@@ -525,48 +578,10 @@ notmuch_reply_format_default(void *ctx,
 {
message = notmuch_messages_get (messages);

-   /* The 1 means we want headers in a "pretty" order. */
-   reply = g_mime_message_new (1);
-   if (reply == NULL) {
-   fprintf (stderr, "Out of memory\n");
-   return 1;
-   }
-
-   subject = notmuch_message_get_header (message, "subject");
-   if (subject) {
-   if (strncasecmp (subject, "Re:", 3))
-   subject = talloc_asprintf (ctx, "Re: %s", subject);
-   g_mime_message_set_subject (reply, subject);
-   }
-
-   from_addr = add_recipients_from_message (reply, config, message,
-reply_all);
-
-   if (from_addr == NULL)
-   from_addr = guess_from_received_header (config, message);
-
-   if (from_addr == NULL)
-   from_addr = notmuch_config_get_user_primary_email (config);
+   reply = create_reply_message (ctx, config, message, 

[PATCH v5.1 1/4] test: Add broken test for the new JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
---

Adjusted the header display to be consistent with the other JSON formats.

 test/multipart |   51 +++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/test/multipart b/test/multipart
index a3036b4..e7abcc2 100755
--- a/test/multipart
+++ b/test/multipart
@@ -589,6 +589,57 @@ Non-text part: text/html
 EOF
 test_expect_equal_file OUTPUT EXPECTED

+test_begin_subtest "'notmuch reply' to a multipart message with json format"
+notmuch reply --format=json 'id:87liy5ap00.fsf at yoom.home.cworth.org' | 
notmuch_json_show_sanitize >OUTPUT
+cat ",
+ "References": " <87liy5ap00.fsf at yoom.home.cworth.org>"},
+ "original": {"id": "X",
+ "match": false,
+ "filename": "Y",
+ "timestamp": 978709437,
+ "date_relative": "2001-01-05",
+ "tags": ["attachment","inbox","signed","unread"],
+ "headers": {"Subject": "Multipart message",
+ "From": "Carl Worth ",
+ "To": "cworth at cworth.org",
+ "Date": "Fri,
+ 05 Jan 2001 15:43:57 +"},
+ "body": [{"id": 1,
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "multipart/mixed",
+ "content": [{"id": 3,
+ "content-type": "message/rfc822",
+ "content": [{"headers": {"Subject": "html message",
+ "From": "Carl Worth ",
+ "To": "cworth at cworth.org",
+ "Date": "Fri,
+ 05 Jan 2001 15:42:57 +"},
+ "body": [{"id": 4,
+ "content-type": "multipart/alternative",
+ "content": [{"id": 5,
+ "content-type": "text/html"},
+ {"id": 6,
+ "content-type": "text/plain",
+ "content": "This is an embedded message,
+ with a multipart/alternative part.\n"}]}]}]},
+ {"id": 7,
+ "content-type": "text/plain",
+ "filename": "Y",
+ "content": "This is a text attachment.\n"},
+ {"id": 8,
+ "content-type": "text/plain",
+ "content": "And this message is signed.\n\n-Carl\n"}]},
+ {"id": 9,
+ "content-type": "application/pgp-signature"}]}]}}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
 test_begin_subtest "'notmuch show --part' does not corrupt a part with CRLF 
pair"
 notmuch show --format=raw --part=3 id:base64-part-with-crlf > crlf.out
 echo -n -e "\xEF\x0D\x0A" > crlf.expected
-- 
1.7.5.4



[PATCH v5 4/4] emacs: Use the new JSON reply format and message-cite-original

2012-02-14 Thread Adam Wolfe Gordon
Using the new JSON reply format allows emacs to quote HTML parts
nicely by using mm-display-part to turn them into displayable text,
then quoting them with message-cite-original. This is very useful for
users who regularly receive HTML-only email.

Use message-mode's message-cite-original function to create the
quoted body for reply messages. In order to make this act like the
existing notmuch defaults, you will need to set the following in
your emacs configuration:

message-citation-line-format "On %a, %d %b %Y, %f wrote:"
message-citation-line-function 'message-insert-formatted-citation-line

The test has been updated to reflect the (ugly) emacs default.
---
 emacs/notmuch-lib.el  |   39 +++
 emacs/notmuch-mua.el  |  123 +++--
 emacs/notmuch-show.el |   24 +-
 test/emacs|  101 +++-
 4 files changed, 228 insertions(+), 59 deletions(-)

diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
index d315f76..3fc7aff 100644
--- a/emacs/notmuch-lib.el
+++ b/emacs/notmuch-lib.el
@@ -21,6 +21,8 @@

 ;; This is an part of an emacs-based interface to the notmuch mail system.

+(eval-when-compile (require 'cl))
+
 (defvar notmuch-command "notmuch"
   "Command to run the notmuch binary.")

@@ -173,6 +175,43 @@ the user hasn't set this variable with the old or new 
value."
   (list 'when (< emacs-major-version 23)
form))

+(defun notmuch-split-content-type (content-type)
+  "Split content/type into 'content' and 'type'"
+  (split-string content-type "/"))
+
+(defun notmuch-match-content-type (t1 t2)
+  "Return t if t1 and t2 are matching content types, taking wildcards into 
account"
+  (let ((st1 (notmuch-split-content-type t1))
+   (st2 (notmuch-split-content-type t2)))
+(if (or (string= (cadr st1) "*")
+   (string= (cadr st2) "*"))
+   (string= (car st1) (car st2))
+  (string= t1 t2
+
+(defvar notmuch-multipart/alternative-discouraged
+  '(
+;; Avoid HTML parts.
+"text/html"
+;; multipart/related usually contain a text/html part and some associated 
graphics.
+"multipart/related"
+))
+
+(defun notmuch-multipart/alternative-choose (types)
+  "Return a list of preferred types from the given list of types"
+  ;; Based on `mm-preferred-alternative-precedence'.
+  (let ((seq types))
+(dolist (pref (reverse notmuch-multipart/alternative-discouraged))
+  (dolist (elem (copy-sequence seq))
+   (when (string-match pref elem)
+ (setq seq (nconc (delete elem seq) (list elem))
+seq))
+
+(defun notmuch-parts-filter-by-type (parts type)
+  "Given a vector of message parts, return a vector containing the ones 
matching the given type."
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) type)
+   vconcat (list part)))
+
 ;; Compatibility functions for versions of emacs before emacs 23.
 ;;
 ;; Both functions here were copied from emacs 23 with the following copyright:
diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 4be7c13..371993f 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -19,11 +19,15 @@
 ;;
 ;; Authors: David Edmondson 

+(require 'json)
 (require 'message)
+(require 'format-spec)

 (require 'notmuch-lib)
 (require 'notmuch-address)

+(eval-when-compile (require 'cl))
+
 ;;

 (defcustom notmuch-mua-send-hook '(notmuch-mua-message-send-hook)
@@ -72,56 +76,105 @@ list."
(push header message-hidden-headers)))
notmuch-mua-hidden-headers))

+(defun notmuch-mua-get-displayed-part (part query-string)
+  (with-temp-buffer
+(if (assq 'content part)
+   (insert (cdr (assq 'content part)))
+  (call-process notmuch-command nil t nil "show" "--format=raw"
+   (format "--part=%s" (cdr (assq 'id part)))
+   query-string))
+
+(let ((handle (mm-make-handle (current-buffer) (list (cdr (assq 
'content-type part)
+ (end-of-orig (point-max)))
+  (mm-display-part handle)
+  (delete-region (point-min) end-of-orig)
+  (buffer-substring (point-min) (point-max)
+
+(defun notmuch-mua-multipart/*-to-list (parts)
+  (loop for part across parts
+   collect (cdr (assq 'content-type part
+
+(defun notmuch-mua-get-quotable-parts (parts)
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) 
"multipart/alternative")
+ append (let* ((subparts (cdr (assq 'content part)))
+   (types (notmuch-mua-multipart/*-to-list subparts))
+   (chosen-type (car (notmuch-multipart/alternative-choose 
types
+  (notmuch-mua-get-quotable-parts 
(notmuch-parts-filter-by-type subparts chosen-type)))
+   else if (notmuch-match-content-type (cdr (assq 'content-type part)) 
"multipart/*")
+ append (notmuch-mua-get-quotable-parts (cdr (assq 'content part)))
+   else if 

[PATCH v5 3/4] man: Update notmuch-reply man page for JSON format.

2012-02-14 Thread Adam Wolfe Gordon
---
 man/man1/notmuch-reply.1 |5 +
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/man/man1/notmuch-reply.1 b/man/man1/notmuch-reply.1
index 5160ece..307abee 100644
--- a/man/man1/notmuch-reply.1
+++ b/man/man1/notmuch-reply.1
@@ -43,6 +43,11 @@ include
 .BR default
 Includes subject and quoted message body.
 .TP
+.BR json
+Produces JSON output containing headers for a reply message and the
+contents of the original message. This output can be used by a client
+to create a reply message intelligently.
+.TP
 .BR headers\-only
 Only produces In\-Reply\-To, References, To, Cc, and Bcc headers.
 .RE
-- 
1.7.5.4



[PATCH v5 2/4] reply: Add a JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
This new JSON format for replies includes headers generated for a
reply message as well as the headers of the original message.  Using
this data, a client can intelligently create a reply. For example, the
emacs client will be able to create replies with quoted HTML parts by
parsing the HTML parts.

Reply now enforces that only one message is returned, as the semantics
of replying to multiple messages are not well-defined.
---
 notmuch-client.h |3 +
 notmuch-reply.c  |  164 --
 notmuch-show.c   |2 +-
 3 files changed, 125 insertions(+), 44 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 60828aa..d28ea07 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -344,6 +344,9 @@ typedef struct mime_node {
 int next_part_num;
 } mime_node_t;

+void
+format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);
+
 /* Construct a new MIME node pointing to the root message part of
  * message.  If cryptoctx is non-NULL, it will be used to verify
  * signatures on any child parts.  If decrypt is true, then cryptoctx
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 6b244e6..979ad86 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -505,6 +505,61 @@ guess_from_received_header (notmuch_config_t *config, 
notmuch_message_t *message
 return NULL;
 }

+static GMimeMessage *
+create_reply_message(void *ctx,
+notmuch_config_t *config,
+notmuch_message_t *message,
+notmuch_bool_t reply_all)
+{
+const char *subject, *from_addr = NULL;
+const char *in_reply_to, *orig_references, *references;
+
+/* The 1 means we want headers in a "pretty" order. */
+GMimeMessage *reply = g_mime_message_new (1);
+if (reply == NULL) {
+   fprintf (stderr, "Out of memory\n");
+   return NULL;
+}
+
+subject = notmuch_message_get_header (message, "subject");
+if (subject) {
+   if (strncasecmp (subject, "Re:", 3))
+   subject = talloc_asprintf (ctx, "Re: %s", subject);
+   g_mime_message_set_subject (reply, subject);
+}
+
+from_addr = add_recipients_from_message (reply, config,
+message, reply_all);
+
+if (from_addr == NULL)
+   from_addr = guess_from_received_header (config, message);
+
+if (from_addr == NULL)
+   from_addr = notmuch_config_get_user_primary_email (config);
+
+from_addr = talloc_asprintf (ctx, "%s <%s>",
+notmuch_config_get_user_name (config),
+from_addr);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ "From", from_addr);
+
+in_reply_to = talloc_asprintf (ctx, "<%s>",
+  notmuch_message_get_message_id (message));
+
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ "In-Reply-To", in_reply_to);
+
+orig_references = notmuch_message_get_header (message, "references");
+references = talloc_asprintf (ctx, "%s%s%s",
+ orig_references ? orig_references : "",
+ orig_references ? " " : "",
+ in_reply_to);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ "References", references);
+
+return reply;
+}
+
 static int
 notmuch_reply_format_default(void *ctx,
 notmuch_config_t *config,
@@ -515,8 +570,6 @@ notmuch_reply_format_default(void *ctx,
 GMimeMessage *reply;
 notmuch_messages_t *messages;
 notmuch_message_t *message;
-const char *subject, *from_addr = NULL;
-const char *in_reply_to, *orig_references, *references;
 const notmuch_show_format_t *format = _reply;

 for (messages = notmuch_query_search_messages (query);
@@ -525,48 +578,10 @@ notmuch_reply_format_default(void *ctx,
 {
message = notmuch_messages_get (messages);

-   /* The 1 means we want headers in a "pretty" order. */
-   reply = g_mime_message_new (1);
-   if (reply == NULL) {
-   fprintf (stderr, "Out of memory\n");
-   return 1;
-   }
-
-   subject = notmuch_message_get_header (message, "subject");
-   if (subject) {
-   if (strncasecmp (subject, "Re:", 3))
-   subject = talloc_asprintf (ctx, "Re: %s", subject);
-   g_mime_message_set_subject (reply, subject);
-   }
-
-   from_addr = add_recipients_from_message (reply, config, message,
-reply_all);
-
-   if (from_addr == NULL)
-   from_addr = guess_from_received_header (config, message);
-
-   if (from_addr == NULL)
-   from_addr = notmuch_config_get_user_primary_email (config);
-
-   from_addr = talloc_asprintf (ctx, "%s <%s>",
-notmuch_config_get_user_name (config),
-

[PATCH v5 1/4] test: Add broken test for the new JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
---
 test/multipart |   51 +++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/test/multipart b/test/multipart
index a3036b4..9651568 100755
--- a/test/multipart
+++ b/test/multipart
@@ -589,6 +589,57 @@ Non-text part: text/html
 EOF
 test_expect_equal_file OUTPUT EXPECTED

+test_begin_subtest "'notmuch reply' to a multipart message with json format"
+notmuch reply --format=json 'id:87liy5ap00.fsf at yoom.home.cworth.org' | 
notmuch_json_show_sanitize >OUTPUT
+cat ",
+ "references": " <87liy5ap00.fsf at yoom.home.cworth.org>"},
+ "original": {"id": "X",
+ "match": false,
+ "filename": "Y",
+ "timestamp": 978709437,
+ "date_relative": "2001-01-05",
+ "tags": ["attachment","inbox","signed","unread"],
+ "headers": {"Subject": "Multipart message",
+ "From": "Carl Worth ",
+ "To": "cworth at cworth.org",
+ "Date": "Fri,
+ 05 Jan 2001 15:43:57 +"},
+ "body": [{"id": 1,
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "multipart/mixed",
+ "content": [{"id": 3,
+ "content-type": "message/rfc822",
+ "content": [{"headers": {"Subject": "html message",
+ "From": "Carl Worth ",
+ "To": "cworth at cworth.org",
+ "Date": "Fri,
+ 05 Jan 2001 15:42:57 +"},
+ "body": [{"id": 4,
+ "content-type": "multipart/alternative",
+ "content": [{"id": 5,
+ "content-type": "text/html"},
+ {"id": 6,
+ "content-type": "text/plain",
+ "content": "This is an embedded message,
+ with a multipart/alternative part.\n"}]}]}]},
+ {"id": 7,
+ "content-type": "text/plain",
+ "filename": "Y",
+ "content": "This is a text attachment.\n"},
+ {"id": 8,
+ "content-type": "text/plain",
+ "content": "And this message is signed.\n\n-Carl\n"}]},
+ {"id": 9,
+ "content-type": "application/pgp-signature"}]}]}}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
 test_begin_subtest "'notmuch show --part' does not corrupt a part with CRLF 
pair"
 notmuch show --format=raw --part=3 id:base64-part-with-crlf > crlf.out
 echo -n -e "\xEF\x0D\x0A" > crlf.expected
-- 
1.7.5.4



[PATCH v5 0/4] Reply enhancements (was: quoting HTML email ...)

2012-02-14 Thread Adam Wolfe Gordon
Hi everyone,

There are relatively few changes from the last version [1], but the JSON
format has big changes again. A summary of all the changes:

* The JSON reply format now uses the new formatter from the show JSON
  format. This means that the MUA will not need to call notmuch show
  for text/* parts, except HTML, as the content of those parts will be
  included in the JSON output.

* The JSON reply format has changed in a few other minor ways, due to
  reusing the show code.

* Because the original message is now included in the reply format, the
  old show behavior of --format=json implies --entire-thread is reinstated.
  I still think this is a weird behavior, but there's no good reason to
  change it.

* The emacs code is simplified a bit by the changes to the JSON format, but
  its behavior is basically the same.

* Man pages and tests changed to reflect the above changes.

Note that, as implied above, this series relies on Austin's rewrite of the
show JSON formatter [2]. I should probably add the reply JSON format to
the devel/schemata file, but I'll leave that for another patch (perhaps
in a series with a news entry if this is deemed ready to push).

Thanks to everyone for the replies on previous versions, and please let
me know if you have any comments. Also, it would be great if others can
test this out on some interesting messages, as my email collection is
not very esoteric.

[1] id:"1328746916-25447-1-git-send-email-awg+notmuch at xvx.ca"
[2] id:"1329240823-7856-1-git-send-email-amdragon at mit.edu"

Adam Wolfe Gordon (4):
  test: Add broken test for the new JSON reply format.
  reply: Add a JSON reply format.
  man: Update notmuch-reply man page for JSON format.
  emacs: Use the new JSON reply format and message-cite-original

 emacs/notmuch-lib.el |   39 +++
 emacs/notmuch-mua.el |  123 +--
 emacs/notmuch-show.el|   24 +--
 man/man1/notmuch-reply.1 |5 ++
 notmuch-client.h |3 +
 notmuch-reply.c  |  164 ++
 notmuch-show.c   |2 +-
 test/emacs   |  101 -
 test/multipart   |   51 
+++
 9 files changed, 365 insertions(+), 103 deletions(-)

-- 
1.7.5.4



[PATCH] emacs: Fix display of highlighted line in notmuch-show

2012-02-14 Thread Jani Nikula
On Tue, 14 Feb 2012 11:24:16 +0100, Michal Sojka  wrote:
> When notmuch-search-line-faces is used to set background color in search
> results, the highlight of the current line is not always displayed
> correctly. This patch fixes that by increasing the priority property of
> the highlight overlay.

One nitpick: The subject line refers to notmuch-show.

BR,
Jani.

> ---
>  emacs/notmuch.el |   15 +++
>  1 files changed, 11 insertions(+), 4 deletions(-)
> 
> diff --git a/emacs/notmuch.el b/emacs/notmuch.el
> index 5b4f1c5..f851c6f 100644
> --- a/emacs/notmuch.el
> +++ b/emacs/notmuch.el
> @@ -249,10 +249,17 @@ For a mouse binding, return nil."
>(set-buffer-modified-p nil)
>(view-buffer (current-buffer) 'kill-buffer-if-not-modified
>  
> -(defcustom notmuch-search-hook '(hl-line-mode)
> +(require 'hl-line)
> +
> +(defun notmuch-hl-line-mode ()
> +  (prog1 (hl-line-mode)
> +(when hl-line-overlay
> +  (overlay-put hl-line-overlay 'priority 1
> +
> +(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
>"List of functions to call when notmuch displays the search results."
>:type 'hook
> -  :options '(hl-line-mode)
> +  :options '(notmuch-hl-line-mode)
>:group 'notmuch-search
>:group 'notmuch-hooks)
>  
> @@ -567,7 +574,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\".
>  the messages that are about to be tagged"
>  
>:type 'hook
> -  :options '(hl-line-mode)
> +  :options '(notmuch-hl-line-mode)
>:group 'notmuch-hooks)
>  
>  (defcustom notmuch-after-tag-hook nil
> @@ -578,7 +585,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\".
>  'query' will be a string containing the search query that determines
>  the messages that were tagged"
>:type 'hook
> -  :options '(hl-line-mode)
> +  :options '(notmuch-hl-line-mode)
>:group 'notmuch-hooks)
>  
>  (defun notmuch-search-set-tags (tags)
> -- 
> 1.7.7.3
> 
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2] emacs: Fix display of highlighted line in notmuch-search

2012-02-14 Thread Michal Sojka
When notmuch-search-line-faces is used to set background color in search
results, the highlight of the current line is not always displayed
correctly. This patch fixes that by increasing the priority property of
the highlight overlay.
---
 emacs/notmuch.el |   15 +++
 1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 5b4f1c5..f851c6f 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -249,10 +249,17 @@ For a mouse binding, return nil."
   (set-buffer-modified-p nil)
   (view-buffer (current-buffer) 'kill-buffer-if-not-modified

-(defcustom notmuch-search-hook '(hl-line-mode)
+(require 'hl-line)
+
+(defun notmuch-hl-line-mode ()
+  (prog1 (hl-line-mode)
+(when hl-line-overlay
+  (overlay-put hl-line-overlay 'priority 1
+
+(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
   "List of functions to call when notmuch displays the search results."
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-search
   :group 'notmuch-hooks)

@@ -567,7 +574,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\".
 the messages that are about to be tagged"

   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)

 (defcustom notmuch-after-tag-hook nil
@@ -578,7 +585,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\".
 'query' will be a string containing the search query that determines
 the messages that were tagged"
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)

 (defun notmuch-search-set-tags (tags)
-- 
1.7.7.3



[PATCH v6 3/3] NEWS: add news section for new.ignore

2012-02-14 Thread Austin Clements
On Mon,  6 Feb 2012 11:28:25 +0200, Tomi Ollila  wrote:
> Added NEWS section 'Mail store folder/file ignore'.
> ---
>  NEWS |6 ++
>  1 files changed, 6 insertions(+), 0 deletions(-)
> 
> diff --git a/NEWS b/NEWS
> index 5c5b645..59da584 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -29,6 +29,12 @@ Tag exclusion
>  
>  notmuch config set search.exclude_tags deleted spam
>  
> +Mail store folder/file ignore
> +
> +  New configuration option `new.ignore` is used to inform notmuch about
> +  folders and files that are not mail files or contain material that is
> +  not desired to be known by notmuch.
> +

This is a bit unwieldy.  How about something based on the config file
comment?

  A new configuration option, `new.ignore`, lets users specify a
  ;-separated list of file and directory names that will not be searched
  for messages by "notmuch new".

Plus, this way it's not so bad that we can't upgrade an existing config
file comment.

>  Emacs Interface
>  ---
>  
> -- 
> 1.7.6.5
> 
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
> 


[PATCH v6 1/3] test: add tests wrt ignoring user-specified files and directories

2012-02-14 Thread Austin Clements
On Mon,  6 Feb 2012 11:28:23 +0200, Tomi Ollila  wrote:
> Files and directories which are specified in 'new.ignore' in the
> config file shouldn't be indexed nor reported by `notmuch new'.
> 
> This is basically Pieter's work with Austin's comments addressed.
> ---
>  test/new |   22 ++
>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> diff --git a/test/new b/test/new
> index 49f390d..5ce8811 100755
> --- a/test/new
> +++ b/test/new
> @@ -153,4 +153,26 @@ rm -rf "${MAIL_DIR}"/two
>  output=$(NOTMUCH_NEW)
>  test_expect_equal "$output" "No new mail. Removed 3 messages."
>  
> +# This test depends that notmuch new has been run at least once.

s/depends/requires/

> +test_begin_subtest "Skip and report non-mail files"
> +generate_message
> +mkdir -p "${MAIL_DIR}"/.git && touch "${MAIL_DIR}"/.git/config
> +touch "${MAIL_DIR}"/ignored_file
> +touch "${MAIL_DIR}"/.ignored_hidden_file
> +output=$(NOTMUCH_NEW 2>&1)
> +test_expect_equal "$output" \
> +"Note: Ignoring non-mail file: ${MAIL_DIR}/.git/config
> +Note: Ignoring non-mail file: ${MAIL_DIR}/.ignored_hidden_file
> +Note: Ignoring non-mail file: ${MAIL_DIR}/ignored_file
> +Added 1 new message to the database."
> +
> +test_begin_subtest "Ignore files and directories specified in new.ignore"
> +test_subtest_known_broken
> +generate_message
> +notmuch config set new.ignore .git ignored_file .ignored_hidden_file
> +touch "${MAIL_DIR}"/.git # change .git's mtime for notmuch new to rescan.
> +output=$(NOTMUCH_NEW 2>&1)
> +test_expect_equal "$output" "Added 1 new message to the database."
> +
> +
>  test_done
> -- 
> 1.7.6.5
> 
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
> 


[PATCH v2] emacs: Add more processing of displayed headers.

2012-02-14 Thread David Edmondson
* david at tethera.net [2012-02-14 Tue 12:30]
> On Mon,  6 Feb 2012 15:39:05 +, David Edmondson  wrote:
>> Wrap headers to the width of the window and indent continuations.
>
> Hi David;
>
> Not sure what this patch is doing in the middle of this thread?
> Is it meant to be part of this series?

No, it's not related to those others. Mistaken 'In-Reply-To'.
-- next part --
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120214/5c549678/attachment.pgp>


[PATCH 8/8] show: Further general simplifications of the JSON formatter

2012-02-14 Thread Austin Clements
---
 notmuch-show.c |   61 ---
 1 files changed, 22 insertions(+), 39 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 07276c7..6a171a4 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -678,9 +678,10 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 GMimeObject *meta = node->envelope_part ?
GMIME_OBJECT (node->envelope_part) : node->part;
 GMimeContentType *content_type = g_mime_object_get_content_type (meta);
-GMimeStream *stream_memory = g_mime_stream_mem_new ();
 const char *cid = g_mime_object_get_content_id (meta);
-GByteArray *part_content;
+const char *filename = GMIME_IS_PART (node->part) ?
+   g_mime_part_get_filename (GMIME_PART (node->part)) : NULL;
+const char *terminator = "";
 int i;

 if (!first)
@@ -688,15 +689,9 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)

 printf ("{\"id\": %d", node->part_num);

-if (node->decrypt_attempted) {
-   printf (", \"encstatus\": [{\"status\": ");
-   if (node->decrypt_success) {
-   printf ("\"good\"");
-   } else {
-   printf ("\"bad\"");
-   }
-   printf ("}]");
-}
+if (node->decrypt_attempted)
+   printf (", \"encstatus\": [{\"status\": \"%s\"}]",
+   node->decrypt_success ? "good" : "bad");

 if (node->verify_attempted) {
printf (", \"sigstatus\": ");
@@ -706,16 +701,13 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 printf (", \"content-type\": %s",
json_quote_str (local, g_mime_content_type_to_string 
(content_type)));

-if (cid != NULL)
-   printf(", \"content-id\": %s", json_quote_str (local, cid));
+if (cid)
+   printf (", \"content-id\": %s", json_quote_str (local, cid));
+
+if (filename)
+   printf (", \"filename\": %s", json_quote_str (local, filename));

 if (GMIME_IS_PART (node->part)) {
-   const char *filename = g_mime_part_get_filename (GMIME_PART 
(node->part));
-   if (filename)
-   printf (", \"filename\": %s", json_quote_str (local, filename));
-}
-
-if (g_mime_content_type_is_type (content_type, "text", "*")) {
/* For non-HTML text parts, we include the content in the
 * JSON. Since JSON must be Unicode, we handle charset
 * decoding here and do not report a charset to the caller.
@@ -730,42 +722,33 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)

if (content_charset != NULL)
printf (", \"content-charset\": %s", json_quote_str (local, 
content_charset));
-   } else {
+   } else if (g_mime_content_type_is_type (content_type, "text", "*")) {
+   GMimeStream *stream_memory = g_mime_stream_mem_new ();
+   GByteArray *part_content;
show_text_part_content (node->part, stream_memory);
part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM 
(stream_memory));

printf (", \"content\": %s", json_quote_chararray (local, (char *) 
part_content->data, part_content->len));
+   g_object_unref (stream_memory);
}
-} else if (g_mime_content_type_is_type (content_type, "multipart", "*")) {
+} else if (GMIME_IS_MULTIPART (node->part)) {
printf (", \"content\": [");
-} else if (g_mime_content_type_is_type (content_type, "message", 
"rfc822")) {
+   terminator = "]";
+} else if (GMIME_IS_MESSAGE (node->part)) {
printf (", \"content\": [{");
-}
-
-if (stream_memory)
-   g_object_unref (stream_memory);
-
-if (GMIME_IS_MESSAGE (node->part)) {
printf ("\"headers\": ");
format_headers_json (local, GMIME_MESSAGE (node->part));

printf (", \"body\": [");
+   terminator = "]}]";
 }

+talloc_free (local);
+
 for (i = 0; i < node->nchildren; i++)
format_part_json (ctx, mime_node_child (node, i), i == 0);

-if (GMIME_IS_MESSAGE (node->part))
-   printf ("]");
-
-if (g_mime_content_type_is_type (content_type, "multipart", "*"))
-   printf ("]");
-else if (g_mime_content_type_is_type (content_type, "message", "rfc822"))
-   printf ("}]");
-
-printf ("}");
-
-talloc_free (local);
+printf ("%s}", terminator);
 }

 static void
-- 
1.7.7.3



[PATCH 7/8] show: Make format_part_sigstatus_json's API consistent between GMIME 2.4 and 2.6

2012-02-14 Thread Austin Clements
The implementation is still different for GMIME 2.4 and 2.6, but at
least now the caller doesn't have to be aware of this.
---
 notmuch-show.c |   14 +++---
 1 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 8fb6fa6..07276c7 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -404,8 +404,10 @@ signer_status_to_string (GMimeSignerStatus x)

 #ifdef GMIME_ATLEAST_26
 static void
-format_part_sigstatus_json (GMimeSignatureList *siglist)
+format_part_sigstatus_json (mime_node_t *node)
 {
+GMimeSignatureList *siglist = node->sig_list;
+
 printf ("[");

 if (!siglist) {
@@ -470,8 +472,10 @@ format_part_sigstatus_json (GMimeSignatureList *siglist)
 }
 #else
 static void
-format_part_sigstatus_json (const GMimeSignatureValidity* validity)
+format_part_sigstatus_json (mime_node_t *node)
 {
+const GMimeSignatureValidity* validity = node->sig_validity;
+
 printf ("[");

 if (!validity) {
@@ -696,11 +700,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)

 if (node->verify_attempted) {
printf (", \"sigstatus\": ");
-#ifdef GMIME_ATLEAST_26
-   format_part_sigstatus_json (node->sig_list);
-#else
-   format_part_sigstatus_json (node->sig_validity);
-#endif
+   format_part_sigstatus_json (node);
 }

 printf (", \"content-type\": %s",
-- 
1.7.7.3



[PATCH 6/8] show: Make JSON helper functions print complete objects

2012-02-14 Thread Austin Clements
This makes the main recursive function easier to follow because helper
functions don't add fields to the running object.
---
 notmuch-show.c |   15 +++
 1 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 6259d30..8fb6fa6 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -297,7 +297,7 @@ format_headers_json (const void *ctx, GMimeMessage *message)
 InternetAddressList *recipients;
 const char *recipients_string;

-printf ("%s: %s",
+printf ("{%s: %s",
json_quote_str (local, "Subject"),
json_quote_str (local, g_mime_message_get_subject (message)));
 printf (", %s: %s",
@@ -315,7 +315,7 @@ format_headers_json (const void *ctx, GMimeMessage *message)
printf (", %s: %s",
json_quote_str (local, "Cc"),
json_quote_str (local, recipients_string));
-printf (", %s: %s",
+printf (", %s: %s}",
json_quote_str (local, "Date"),
json_quote_str (local, g_mime_message_get_date_as_string 
(message)));

@@ -406,7 +406,7 @@ signer_status_to_string (GMimeSignerStatus x)
 static void
 format_part_sigstatus_json (GMimeSignatureList *siglist)
 {
-printf (", \"sigstatus\": [");
+printf ("[");

 if (!siglist) {
printf ("]");
@@ -472,7 +472,7 @@ format_part_sigstatus_json (GMimeSignatureList *siglist)
 static void
 format_part_sigstatus_json (const GMimeSignatureValidity* validity)
 {
-printf (", \"sigstatus\": [");
+printf ("[");

 if (!validity) {
printf ("]");
@@ -658,9 +658,8 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
printf ("{");
format_message_json (ctx, node->envelope_file);

-   printf ("\"headers\": {");
+   printf ("\"headers\": ");
format_headers_json (ctx, GMIME_MESSAGE (node->part));
-   printf ("}");

printf (", \"body\": [");
format_part_json (ctx, mime_node_child (node, 0), first);
@@ -696,6 +695,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 }

 if (node->verify_attempted) {
+   printf (", \"sigstatus\": ");
 #ifdef GMIME_ATLEAST_26
format_part_sigstatus_json (node->sig_list);
 #else
@@ -746,9 +746,8 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
g_object_unref (stream_memory);

 if (GMIME_IS_MESSAGE (node->part)) {
-   printf ("\"headers\": {");
+   printf ("\"headers\": ");
format_headers_json (local, GMIME_MESSAGE (node->part));
-   printf ("}");

printf (", \"body\": [");
 }
-- 
1.7.7.3



[PATCH 4/8] show: Unify JSON header output for messages and message parts

2012-02-14 Thread Austin Clements
This has three ramifications:
- Blank To and Cc headers are no longer output for messages.
- Dates are now canonicalized for messages, which means they always
  have a day of the week and GMT is printed + (never -)
- Invalid From message headers are handled slightly differently, since
  they get parsed by GMime now instead of notmuch.
---
 notmuch-show.c|   35 +++
 test/crypto   |   35 ++-
 test/emacs|4 ++--
 test/json |6 +++---
 test/maildir-sync |2 --
 test/multipart|2 +-
 6 files changed, 23 insertions(+), 61 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 9ca9882..209ff45 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -291,36 +291,7 @@ format_headers_message_part_text (GMimeMessage *message)
 }

 static void
-format_headers_json (const void *ctx, notmuch_message_t *message)
-{
-const char *headers[] = {
-   "Subject", "From", "To", "Cc", "Bcc", "Date"
-};
-const char *name, *value;
-unsigned int i;
-int first_header = 1;
-void *ctx_quote = talloc_new (ctx);
-
-for (i = 0; i < ARRAY_SIZE (headers); i++) {
-   name = headers[i];
-   value = notmuch_message_get_header (message, name);
-   if (value)
-   {
-   if (!first_header)
-   fputs (", ", stdout);
-   first_header = 0;
-
-   printf ("%s: %s",
-   json_quote_str (ctx_quote, name),
-   json_quote_str (ctx_quote, value));
-   }
-}
-
-talloc_free (ctx_quote);
-}
-
-static void
-format_headers_message_part_json (GMimeMessage *message)
+format_headers_json (GMimeMessage *message)
 {
 void *ctx = talloc_new (NULL);
 void *ctx_quote = talloc_new (ctx);
@@ -690,7 +661,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
format_message_json (ctx, node->envelope_file);

printf ("\"headers\": {");
-   format_headers_json (ctx, node->envelope_file);
+   format_headers_json (GMIME_MESSAGE (node->part));
printf ("}");

printf (", \"body\": [");
@@ -778,7 +749,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)

 if (GMIME_IS_MESSAGE (node->part)) {
printf ("\"headers\": {");
-   format_headers_message_part_json (GMIME_MESSAGE (node->part));
+   format_headers_json (GMIME_MESSAGE (node->part));
printf ("}");

printf (", \"body\": [");
diff --git a/test/crypto b/test/crypto
index 1dbb60a..7e774c8 100755
--- a/test/crypto
+++ b/test/crypto
@@ -50,9 +50,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": "test signed message 001",
  "From": "Notmuch Test Suite ",
  "To": "test_suite at notmuchmail.org",
- "Cc": "",
- "Bcc": "",
- "Date": "01 Jan 2000 12:00:00 -"},
+ "Date": "Sat,
+ 01 Jan 2000 12:00:00 +"},
  "body": [{"id": 1,
  "sigstatus": [{"status": "good",
  "fingerprint": "'$FINGERPRINT'",
@@ -84,9 +83,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": "test signed message 001",
  "From": "Notmuch Test Suite ",
  "To": "test_suite at notmuchmail.org",
- "Cc": "",
- "Bcc": "",
- "Date": "01 Jan 2000 12:00:00 -"},
+ "Date": "Sat,
+ 01 Jan 2000 12:00:00 +"},
  "body": [{"id": 1,
  "sigstatus": [{"status": "good",
  "fingerprint": "'$FINGERPRINT'",
@@ -120,9 +118,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": "test signed message 001",
  "From": "Notmuch Test Suite ",
  "To": "test_suite at notmuchmail.org",
- "Cc": "",
- "Bcc": "",
- "Date": "01 Jan 2000 12:00:00 -"},
+ "Date": "Sat,
+ 01 Jan 2000 12:00:00 +"},
  "body": [{"id": 1,
  "sigstatus": [{"status": "error",
  "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",
@@ -194,9 +191,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": "test encrypted message 001",
  "From": "Notmuch Test Suite ",
  "To": "test_suite at notmuchmail.org",
- "Cc": "",
- "Bcc": "",
- "Date": "01 Jan 2000 12:00:00 -"},
+ "Date": "Sat,
+ 01 Jan 2000 12:00:00 +"},
  "body": [{"id": 1,
  "encstatus": [{"status": "good"}],
  "sigstatus": [],
@@ -249,9 +245,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": "test encrypted message 001",
  "From": "Notmuch Test Suite ",
  "To": "test_suite at notmuchmail.org",
- "Cc": "",
- "Bcc": "",
- "Date": "01 Jan 2000 12:00:00 -"},
+ "Date": "Sat,
+ 01 Jan 2000 12:00:00 +"},
  "body": [{"id": 1,
  "encstatus": [{"status": "bad"}],
  "content-type": "multipart/encrypted",
@@ -284,9 +279,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": "test encrypted message 002",
  "From": "Notmuch Test Suite ",
  "To": "test_suite at notmuchmail.org",
- "Cc": "",
- "Bcc": "",
- "Date": "01 Jan 2000 12:00:00 -"},
+ "Date": "Sat,
+ 01 Jan 2000 12:00:00 +"},
  "body": [{"id": 1,
  "encstatus": [{"status": "good"}],
  "sigstatus": [{"status": "good",
@@ -339,9 +333,8 @@ expected='[[[{"id": "X",
  "headers": {"Subject": 

[PATCH 1/8] Document the JSON schemata used by show and search

2012-02-14 Thread Austin Clements
---
 devel/schemata   |  135 ++
 notmuch-search.c |3 +
 notmuch-show.c   |2 +
 3 files changed, 140 insertions(+), 0 deletions(-)
 create mode 100644 devel/schemata

diff --git a/devel/schemata b/devel/schemata
new file mode 100644
index 000..d90d4c6
--- /dev/null
+++ b/devel/schemata
@@ -0,0 +1,135 @@
+This file describes the schemata used for notmuch's structured output
+format (currently JSON).
+
+[]'s indicate lists.  List items can be marked with a '?', meaning
+they are optional; or a '*', meaning there can be zero or more of that
+item.  {}'s indicate an object that maps from field identifiers to
+values.  An object field marked '?' is optional.  |'s indicate
+alternates (e.g., int|string means something can be an int or a
+string).
+
+Common non-terminals
+
+
+# Number of seconds since the Epoch
+unix_time = int
+
+# Thread ID, sans "thread:"
+threadid = string
+
+# Message ID, sans "id:"
+messageid = string
+
+notmuch show schema
+---
+
+# A top-level set of threads (do_show)
+# Returned by notmuch show without a --part argument
+thread_set = [thread*]
+
+# Top-level messages in a thread (show_messages)
+thread = [thread_node*]
+
+# A message and its replies (show_messages)
+thread_node = [
+message?, # present if --entire-thread or matched
+[thread_node*]# children of message
+]
+
+# A message (show_message)
+message = {
+# (format_message_json)
+id: messageid,
+match:  bool,
+filename:  string,
+timestamp:  unix_time, # date header as unix time
+date_relative:  string,   # user-friendly timestamp
+tags:   [string*],
+
+headers:headers,
+body:   [part]
+}
+
+# A MIME part (show_message_body)
+part = {
+# format_part_start_json
+id: int|string, # part id (currently DFS part number)
+
+# format_part_encstatus_json
+encstatus?: encstatus,
+
+# format_part_sigstatus_json
+sigstatus?: sigstatus,
+
+# format_part_content_json
+content-type:   string,
+content-id?:string,
+# if content-type starts with "multipart/":
+content:[part*],
+# if content-type is "message/rfc822":
+content:[{headers: headers, body: [part]}],
+# otherwise (leaf parts):
+filename?:  string,
+content-charset?: string,
+content?:   string# pre-fetched body content
+}
+
+# The headers of a message (format_headers_json with raw headers) or
+# a part (format_headers_message_part_json with pretty-printed headers)
+headers = {
+Subject:string,
+From:   string,
+To?:string,
+Cc?:string,
+Bcc?:   string,
+Date:   string
+}
+
+# Encryption status (format_part_encstatus_json)
+encstatus = [{status: "good"|"bad"}]
+
+# Signature status (format_part_sigstatus_json)
+sigstatus = [signature*]
+
+signature = {
+# signature_status_to_string
+status: "none"|"good"|"bad"|"error"|"unknown",
+# if status is "good":
+fingerprint?:   string,
+created?:   unix_time,
+expires?:   unix_time,
+userid?:string
+# if status is not "good":
+keyid?: string
+# if the signature has errors:
+errors?:int
+}
+
+notmuch search schema
+-
+
+# --output=summary
+summary = [thread*]
+
+# --output=threads
+threads = [threadid*]
+
+# --output=messages
+messages = [messageid*]
+
+# --output=files
+files = [string*]
+
+# --output=tags
+tags = [string*]
+
+thread = {
+thread: threadid,
+timestamp:  unix_time,
+date_relative:  string,   # user-friendly timestamp
+matched:int,  # number of matched messages
+total:  int,  # total messages in thread
+authors:string,   # comma-separated names with | between 
+  # matched and unmatched
+subject:string
+}
diff --git a/notmuch-search.c b/notmuch-search.c
index d504051..92ce38a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -90,6 +90,9 @@ format_thread_json (const void *ctx,
const int total,
const char *authors,
const char *subject);
+
+/* Any changes to the JSON format should be reflected in the file
+ * devel/schemata. */
 static const search_format_t format_json = {
 "[",
"{",
diff --git a/notmuch-show.c b/notmuch-show.c
index d930f94..93fb16f 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -65,6 +65,8 @@ format_part_content_json (GMimeObject *part);
 static void
 format_part_end_json (GMimeObject *part);

+/* Any changes to the JSON format should be reflected in the file
+ * devel/schemata. */
 static const notmuch_show_format_t format_json = {
 "[", NULL,
"{", format_message_json,
-- 
1.7.7.3



[PATCH 0/8] Rewrite JSON show format

2012-02-14 Thread Austin Clements
The saga continues.  As for the text format, this first shifts lots of
code around without changing its semantics, then it dives in and
simplifies a lot of things.  Don't be put off by the number of
patches; most of them are straightforward.

As an added bonus, I documented (!) the JSON format for both show and
search.

Thanks to Adam for the kick in the tail I needed to put the finishing
touches on this series.



[RFC PATCH v3 00/11] notmuch-pick: an emacs threaded message view with split-pane

2012-02-14 Thread Mark Walters

I have now done some benchmarking/profiling of the notmuch-pick
mode. These are all done running locally (i.e., no ssh, nfs or anything)
but on a fairly old computer with a slow hard disk. The timings given
are for the second run (so the files and database are in cache).

The profiling is done by inserting (message "%s" (current-time)) at
various points.

For displaying the thread structure of the whole notmuch mailing list
archive (circa 10,000 messages) it takes about 18 seconds which breaks
down as 6.5 seconds for the cli part, 9.5 seconds for the json parsing
in emacs and 1.5 seconds for constructing the thread structure and
writing the buffer in emacs.

Using Austin's optimised json.el (id:"20110720205007.GB21316 at mit.edu")
the 9.5 seconds reduces to about 2.5 seconds (so the total reduces to
about 10.5 seconds).

I think the 6.5 seconds of command line time involves quite a lot of
parsing of the message files (to get the headers) and this could
probably be sped up by storing more of the headers in the database, or
just not outputting all of them. Removing all of the calls to
notmuch_message_get_header seems to reduce the 6.5s to about 1.5s

In other cases, though, notmuch-pick is a noticeable speed win: namely
`picking' is faster than `showing' for longer threads (as one would
expect since pick only gets the thread structure and one message body
whereas show gets the thread structure and all messages bodies). So for
a thread of 170 messages show takes 5 seconds and pick takes less than
0.5 seconds, and for a thread of 30 messages show takes 600 milliseconds
and pick takes takes 150 milliseconds.

Finally, if notmuch-pick were able to do work asynchronously (as
notmuch-search does now) then I think all the speed concerns would go
away. However, I am not sure how to do incremental json parsing.

Best wishes

Mark




[PATCH] emacs: Fix display of highlighted line in notmuch-show

2012-02-14 Thread Michal Sojka
When notmuch-search-line-faces is used to set background color in search
results, the highlight of the current line is not always displayed
correctly. This patch fixes that by increasing the priority property of
the highlight overlay.
---
 emacs/notmuch.el |   15 +++
 1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 5b4f1c5..f851c6f 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -249,10 +249,17 @@ For a mouse binding, return nil."
   (set-buffer-modified-p nil)
   (view-buffer (current-buffer) 'kill-buffer-if-not-modified

-(defcustom notmuch-search-hook '(hl-line-mode)
+(require 'hl-line)
+
+(defun notmuch-hl-line-mode ()
+  (prog1 (hl-line-mode)
+(when hl-line-overlay
+  (overlay-put hl-line-overlay 'priority 1
+
+(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
   "List of functions to call when notmuch displays the search results."
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-search
   :group 'notmuch-hooks)

@@ -567,7 +574,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\".
 the messages that are about to be tagged"

   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)

 (defcustom notmuch-after-tag-hook nil
@@ -578,7 +585,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\".
 'query' will be a string containing the search query that determines
 the messages that were tagged"
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)

 (defun notmuch-search-set-tags (tags)
-- 
1.7.7.3



notmuch-emacs bug report -- infinite looping trying to select next message

2012-02-14 Thread Michael Hudson-Doyle
The attached gzipped mbox appears to trip up the emacs interface.  The
problem seems to come from the message with id
CAGNsrLCWv6=36q+q+5Hc_SzgdZ2ergeKkapT7T3xXvim=2cK+A at mail.gmail.com.

If you load up the thread in emacs, you get a message:

mm-extern-cache-contents: Couldn't find access type

Then attempting to advance past the last display message (or pressing A,
or a few other things I expect gets emacs to loop indefinitely.
toggle-debug-on-quit gets me this backtrace:

Debugger entered--Lisp error: (quit)
  notmuch-show-message-extent()
  notmuch-show-message-bottom()
  notmuch-show-move-to-message-bottom()
  notmuch-show-goto-message-next()
  notmuch-show-next-open-message(nil)
  call-interactively(notmuch-show-next-open-message nil nil)

Looking at the suspect message one sees this:

--20cf305b0f1a1caaf604b875ea95
Content-Type: message/external-body; access-type=x-mutt-deleted;
expiration="Wed, 8 Feb 2012 08:45:08 -0800"; length=644023

Content-Type: text/x-log; charset=US-ASCII; name="binary.log"
Content-Disposition: attachment; filename="binary.log"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_gyejghyz0


--20cf305b0f1a1caaf604b875ea95
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

which looks a bit odd to me (maybe mailman stripped an attachment), but
I don't know much about MIME :-)

Cheers,
mwh

-- next part --
A non-text attachment was scrubbed...
Name: notmuch-el-hang.mbox.gz
Type: application/octet-stream
Size: 25936 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20120214/21ad530d/attachment-0001.obj>


notmuch-emacs bug report -- infinite looping trying to select next message

2012-02-14 Thread Rodney Lorrimar
Hi Michael,

On Tue, 14 Feb 2012 11:01:56 +1300, Michael Hudson-Doyle  wrote:
> The attached gzipped mbox appears to trip up the emacs interface.  The
> problem seems to come from the message with id
> CAGNsrLCWv6=36q+q+5Hc_SzgdZ2ergeKkapT7T3xXvim=2cK+A at mail.gmail.com.
> 
> If you load up the thread in emacs, you get a message:
> 
> mm-extern-cache-contents: Couldn't find access type

If you put (require 'gnus-art) into your .emacs and eval it, does the
problem go away?

I had a similar problem when running a newer emacs-snapshot with
notmuch. See this thread: id:"87d3aaqyur.fsf at eve.chaoflow.net"

> Then attempting to advance past the last display message (or pressing A,
> or a few other things I expect gets emacs to loop indefinitely.
> toggle-debug-on-quit gets me this backtrace:

I believe the actual bug is in gnus but I don't really like the loop on
error behaviour of notmuch. I would like to try and fix it but haven't
found the time.


Cheers,

Rodney


[PATCH] emacs: Fix display of highlighted line in notmuch-show

2012-02-14 Thread Austin Clements
Quoth Michal Sojka on Feb 14 at 11:24 am:
> When notmuch-search-line-faces is used to set background color in search
> results, the highlight of the current line is not always displayed
> correctly. This patch fixes that by increasing the priority property of
> the highlight overlay.

LGTM.  Too bad hl-line doesn't have a mode hook we could do this in.


[RFC PATCH v3 00/11] notmuch-pick: an emacs threaded message view with split-pane

2012-02-14 Thread Austin Clements
Quoth Mark Walters on Feb 14 at 12:28 pm:
> Finally, if notmuch-pick were able to do work asynchronously (as
> notmuch-search does now) then I think all the speed concerns would go
> away. However, I am not sure how to do incremental json parsing.

For JSON search, at least, I think we've concluded that it should put
newlines at strategic places in the JSON output (and never anywhere
else) so that it's easy for a consumer to know when it has a complete
JSON object and hand it to the JSON parser.  E.g.,

[{"thread":"X", timestamp: 42, ...}
,{"thread":"Y", timestamp: 24, ...}
]

This "framed JSON" works really well for the flat output of search.
It's obviously trickier to apply to show's hierarchical output.  But,
perhaps this will inspire you.

(One possibility: we could rework show's output to be non-hierarchical
and use the above framing; that is, it could be a sequence of message
objects that each indicate which earlier message object they're a
reply to, probably restricted to DFS order.)


[PATCH v2] emacs: Add more processing of displayed headers.

2012-02-14 Thread David Bremner
On Mon,  6 Feb 2012 15:39:05 +, David Edmondson  wrote:
> Wrap headers to the width of the window and indent continuations.

Hi David;

Not sure what this patch is doing in the middle of this thread?
Is it meant to be part of this series?

d


[PATCH 0/2] Rewrite text show format

2012-02-14 Thread Adam Wolfe Gordon
Hi Austin,

On Wed, Jan 25, 2012 at 23:55, Austin Clements  wrote:
> And now the real fun begins. ?This series translates the text
> formatter into the new format style in two steps: the first patch is a
> big diff but just shuffles code and the second actually takes
> advantage of the new structure.

Thanks for this work. I'm now rewriting the JSON format to use the new
structure, which will help simplify my reply patches.


Re: notmuch-emacs bug report -- infinite looping trying to select next message

2012-02-14 Thread Rodney Lorrimar
Hi Michael,

On Tue, 14 Feb 2012 11:01:56 +1300, Michael Hudson-Doyle 
michael.hud...@canonical.com wrote:
 The attached gzipped mbox appears to trip up the emacs interface.  The
 problem seems to come from the message with id
 CAGNsrLCWv6=36q+q+5Hc_SzgdZ2ergeKkapT7T3xXvim=2c...@mail.gmail.com.
 
 If you load up the thread in emacs, you get a message:
 
 mm-extern-cache-contents: Couldn't find access type

If you put (require 'gnus-art) into your .emacs and eval it, does the
problem go away?

I had a similar problem when running a newer emacs-snapshot with
notmuch. See this thread: id:87d3aaqyur@eve.chaoflow.net

 Then attempting to advance past the last display message (or pressing A,
 or a few other things I expect gets emacs to loop indefinitely.
 toggle-debug-on-quit gets me this backtrace:

I believe the actual bug is in gnus but I don't really like the loop on
error behaviour of notmuch. I would like to try and fix it but haven't
found the time.


Cheers,

Rodney
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] emacs: Fix display of highlighted line in notmuch-show

2012-02-14 Thread Michal Sojka
When notmuch-search-line-faces is used to set background color in search
results, the highlight of the current line is not always displayed
correctly. This patch fixes that by increasing the priority property of
the highlight overlay.
---
 emacs/notmuch.el |   15 +++
 1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 5b4f1c5..f851c6f 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -249,10 +249,17 @@ For a mouse binding, return nil.
   (set-buffer-modified-p nil)
   (view-buffer (current-buffer) 'kill-buffer-if-not-modified
 
-(defcustom notmuch-search-hook '(hl-line-mode)
+(require 'hl-line)
+
+(defun notmuch-hl-line-mode ()
+  (prog1 (hl-line-mode)
+(when hl-line-overlay
+  (overlay-put hl-line-overlay 'priority 1
+
+(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
   List of functions to call when notmuch displays the search results.
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-search
   :group 'notmuch-hooks)
 
@@ -567,7 +574,7 @@ a list of strings of the form \+TAG\ or \-TAG\.
 the messages that are about to be tagged
 
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)
 
 (defcustom notmuch-after-tag-hook nil
@@ -578,7 +585,7 @@ a list of strings of the form \+TAG\ or \-TAG\.
 'query' will be a string containing the search query that determines
 the messages that were tagged
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)
 
 (defun notmuch-search-set-tags (tags)
-- 
1.7.7.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2] emacs: Add more processing of displayed headers.

2012-02-14 Thread David Bremner
On Mon,  6 Feb 2012 15:39:05 +, David Edmondson d...@dme.org wrote:
 Wrap headers to the width of the window and indent continuations.

Hi David;

Not sure what this patch is doing in the middle of this thread?
Is it meant to be part of this series?

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 0/2] Rewrite text show format

2012-02-14 Thread Adam Wolfe Gordon
Hi Austin,

On Wed, Jan 25, 2012 at 23:55, Austin Clements amdra...@mit.edu wrote:
 And now the real fun begins.  This series translates the text
 formatter into the new format style in two steps: the first patch is a
 big diff but just shuffles code and the second actually takes
 advantage of the new structure.

Thanks for this work. I'm now rewriting the JSON format to use the new
structure, which will help simplify my reply patches.
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [RFC PATCH v3 00/11] notmuch-pick: an emacs threaded message view with split-pane

2012-02-14 Thread Austin Clements
Quoth Mark Walters on Feb 14 at 12:28 pm:
 Finally, if notmuch-pick were able to do work asynchronously (as
 notmuch-search does now) then I think all the speed concerns would go
 away. However, I am not sure how to do incremental json parsing.

For JSON search, at least, I think we've concluded that it should put
newlines at strategic places in the JSON output (and never anywhere
else) so that it's easy for a consumer to know when it has a complete
JSON object and hand it to the JSON parser.  E.g.,

[{thread:X, timestamp: 42, ...}
,{thread:Y, timestamp: 24, ...}
]

This framed JSON works really well for the flat output of search.
It's obviously trickier to apply to show's hierarchical output.  But,
perhaps this will inspire you.

(One possibility: we could rework show's output to be non-hierarchical
and use the above framing; that is, it could be a sequence of message
objects that each indicate which earlier message object they're a
reply to, probably restricted to DFS order.)
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] emacs: Fix display of highlighted line in notmuch-show

2012-02-14 Thread Jani Nikula
On Tue, 14 Feb 2012 11:24:16 +0100, Michal Sojka so...@os.inf.tu-dresden.de 
wrote:
 When notmuch-search-line-faces is used to set background color in search
 results, the highlight of the current line is not always displayed
 correctly. This patch fixes that by increasing the priority property of
 the highlight overlay.

One nitpick: The subject line refers to notmuch-show.

BR,
Jani.

 ---
  emacs/notmuch.el |   15 +++
  1 files changed, 11 insertions(+), 4 deletions(-)
 
 diff --git a/emacs/notmuch.el b/emacs/notmuch.el
 index 5b4f1c5..f851c6f 100644
 --- a/emacs/notmuch.el
 +++ b/emacs/notmuch.el
 @@ -249,10 +249,17 @@ For a mouse binding, return nil.
(set-buffer-modified-p nil)
(view-buffer (current-buffer) 'kill-buffer-if-not-modified
  
 -(defcustom notmuch-search-hook '(hl-line-mode)
 +(require 'hl-line)
 +
 +(defun notmuch-hl-line-mode ()
 +  (prog1 (hl-line-mode)
 +(when hl-line-overlay
 +  (overlay-put hl-line-overlay 'priority 1
 +
 +(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
List of functions to call when notmuch displays the search results.
:type 'hook
 -  :options '(hl-line-mode)
 +  :options '(notmuch-hl-line-mode)
:group 'notmuch-search
:group 'notmuch-hooks)
  
 @@ -567,7 +574,7 @@ a list of strings of the form \+TAG\ or \-TAG\.
  the messages that are about to be tagged
  
:type 'hook
 -  :options '(hl-line-mode)
 +  :options '(notmuch-hl-line-mode)
:group 'notmuch-hooks)
  
  (defcustom notmuch-after-tag-hook nil
 @@ -578,7 +585,7 @@ a list of strings of the form \+TAG\ or \-TAG\.
  'query' will be a string containing the search query that determines
  the messages that were tagged
:type 'hook
 -  :options '(hl-line-mode)
 +  :options '(notmuch-hl-line-mode)
:group 'notmuch-hooks)
  
  (defun notmuch-search-set-tags (tags)
 -- 
 1.7.7.3
 
 ___
 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 v2] emacs: Fix display of highlighted line in notmuch-search

2012-02-14 Thread Michal Sojka
When notmuch-search-line-faces is used to set background color in search
results, the highlight of the current line is not always displayed
correctly. This patch fixes that by increasing the priority property of
the highlight overlay.
---
 emacs/notmuch.el |   15 +++
 1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 5b4f1c5..f851c6f 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -249,10 +249,17 @@ For a mouse binding, return nil.
   (set-buffer-modified-p nil)
   (view-buffer (current-buffer) 'kill-buffer-if-not-modified
 
-(defcustom notmuch-search-hook '(hl-line-mode)
+(require 'hl-line)
+
+(defun notmuch-hl-line-mode ()
+  (prog1 (hl-line-mode)
+(when hl-line-overlay
+  (overlay-put hl-line-overlay 'priority 1
+
+(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
   List of functions to call when notmuch displays the search results.
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-search
   :group 'notmuch-hooks)
 
@@ -567,7 +574,7 @@ a list of strings of the form \+TAG\ or \-TAG\.
 the messages that are about to be tagged
 
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)
 
 (defcustom notmuch-after-tag-hook nil
@@ -578,7 +585,7 @@ a list of strings of the form \+TAG\ or \-TAG\.
 'query' will be a string containing the search query that determines
 the messages that were tagged
   :type 'hook
-  :options '(hl-line-mode)
+  :options '(notmuch-hl-line-mode)
   :group 'notmuch-hooks)
 
 (defun notmuch-search-set-tags (tags)
-- 
1.7.7.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 0/8] Rewrite JSON show format

2012-02-14 Thread Austin Clements
The saga continues.  As for the text format, this first shifts lots of
code around without changing its semantics, then it dives in and
simplifies a lot of things.  Don't be put off by the number of
patches; most of them are straightforward.

As an added bonus, I documented (!) the JSON format for both show and
search.

Thanks to Adam for the kick in the tail I needed to put the finishing
touches on this series.

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/8] Document the JSON schemata used by show and search

2012-02-14 Thread Austin Clements
---
 devel/schemata   |  135 ++
 notmuch-search.c |3 +
 notmuch-show.c   |2 +
 3 files changed, 140 insertions(+), 0 deletions(-)
 create mode 100644 devel/schemata

diff --git a/devel/schemata b/devel/schemata
new file mode 100644
index 000..d90d4c6
--- /dev/null
+++ b/devel/schemata
@@ -0,0 +1,135 @@
+This file describes the schemata used for notmuch's structured output
+format (currently JSON).
+
+[]'s indicate lists.  List items can be marked with a '?', meaning
+they are optional; or a '*', meaning there can be zero or more of that
+item.  {}'s indicate an object that maps from field identifiers to
+values.  An object field marked '?' is optional.  |'s indicate
+alternates (e.g., int|string means something can be an int or a
+string).
+
+Common non-terminals
+
+
+# Number of seconds since the Epoch
+unix_time = int
+
+# Thread ID, sans thread:
+threadid = string
+
+# Message ID, sans id:
+messageid = string
+
+notmuch show schema
+---
+
+# A top-level set of threads (do_show)
+# Returned by notmuch show without a --part argument
+thread_set = [thread*]
+
+# Top-level messages in a thread (show_messages)
+thread = [thread_node*]
+
+# A message and its replies (show_messages)
+thread_node = [
+message?, # present if --entire-thread or matched
+[thread_node*]# children of message
+]
+
+# A message (show_message)
+message = {
+# (format_message_json)
+id: messageid,
+match:  bool,
+filename:  string,
+timestamp:  unix_time, # date header as unix time
+date_relative:  string,   # user-friendly timestamp
+tags:   [string*],
+
+headers:headers,
+body:   [part]
+}
+
+# A MIME part (show_message_body)
+part = {
+# format_part_start_json
+id: int|string, # part id (currently DFS part number)
+
+# format_part_encstatus_json
+encstatus?: encstatus,
+
+# format_part_sigstatus_json
+sigstatus?: sigstatus,
+
+# format_part_content_json
+content-type:   string,
+content-id?:string,
+# if content-type starts with multipart/:
+content:[part*],
+# if content-type is message/rfc822:
+content:[{headers: headers, body: [part]}],
+# otherwise (leaf parts):
+filename?:  string,
+content-charset?: string,
+content?:   string# pre-fetched body content
+}
+
+# The headers of a message (format_headers_json with raw headers) or
+# a part (format_headers_message_part_json with pretty-printed headers)
+headers = {
+Subject:string,
+From:   string,
+To?:string,
+Cc?:string,
+Bcc?:   string,
+Date:   string
+}
+
+# Encryption status (format_part_encstatus_json)
+encstatus = [{status: good|bad}]
+
+# Signature status (format_part_sigstatus_json)
+sigstatus = [signature*]
+
+signature = {
+# signature_status_to_string
+status: none|good|bad|error|unknown,
+# if status is good:
+fingerprint?:   string,
+created?:   unix_time,
+expires?:   unix_time,
+userid?:string
+# if status is not good:
+keyid?: string
+# if the signature has errors:
+errors?:int
+}
+
+notmuch search schema
+-
+
+# --output=summary
+summary = [thread*]
+
+# --output=threads
+threads = [threadid*]
+
+# --output=messages
+messages = [messageid*]
+
+# --output=files
+files = [string*]
+
+# --output=tags
+tags = [string*]
+
+thread = {
+thread: threadid,
+timestamp:  unix_time,
+date_relative:  string,   # user-friendly timestamp
+matched:int,  # number of matched messages
+total:  int,  # total messages in thread
+authors:string,   # comma-separated names with | between 
+  # matched and unmatched
+subject:string
+}
diff --git a/notmuch-search.c b/notmuch-search.c
index d504051..92ce38a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -90,6 +90,9 @@ format_thread_json (const void *ctx,
const int total,
const char *authors,
const char *subject);
+
+/* Any changes to the JSON format should be reflected in the file
+ * devel/schemata. */
 static const search_format_t format_json = {
 [,
{,
diff --git a/notmuch-show.c b/notmuch-show.c
index d930f94..93fb16f 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -65,6 +65,8 @@ format_part_content_json (GMimeObject *part);
 static void
 format_part_end_json (GMimeObject *part);
 
+/* Any changes to the JSON format should be reflected in the file
+ * devel/schemata. */
 static const notmuch_show_format_t format_json = {
 [, NULL,
{, format_message_json,
-- 
1.7.7.3


[PATCH 3/8] show: Use consistent header ordering in the JSON format

2012-02-14 Thread Austin Clements
Previously, top-level message headers were printed as Subject, From,
To, Date, while embedded message headers were printed From, To,
Subject, Date.  This makes both cases use the former order and updates
the tests accordingly.
---
 notmuch-show.c |6 +++---
 test/multipart |8 
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 868b2cd..9ca9882 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -328,6 +328,9 @@ format_headers_message_part_json (GMimeMessage *message)
 const char *recipients_string;
 
 printf (%s: %s,
+   json_quote_str (ctx_quote, Subject),
+   json_quote_str (ctx_quote, g_mime_message_get_subject (message)));
+printf (, %s: %s,
json_quote_str (ctx_quote, From),
json_quote_str (ctx_quote, g_mime_message_get_sender (message)));
 recipients = g_mime_message_get_recipients (message, 
GMIME_RECIPIENT_TYPE_TO);
@@ -343,9 +346,6 @@ format_headers_message_part_json (GMimeMessage *message)
json_quote_str (ctx_quote, Cc),
json_quote_str (ctx_quote, recipients_string));
 printf (, %s: %s,
-   json_quote_str (ctx_quote, Subject),
-   json_quote_str (ctx_quote, g_mime_message_get_subject (message)));
-printf (, %s: %s,
json_quote_str (ctx_quote, Date),
json_quote_str (ctx_quote, g_mime_message_get_date_as_string 
(message)));
 
diff --git a/test/multipart b/test/multipart
index 2dd73f5..4d14804 100755
--- a/test/multipart
+++ b/test/multipart
@@ -325,7 +325,7 @@ cat EOF EXPECTED
 {id: 87liy5ap00@yoom.home.cworth.org, match: true, filename: 
${MAIL_DIR}/multipart, timestamp: 978709437, date_relative: 2001-01-05, 
tags: [attachment,inbox,signed,unread], headers: {Subject: 
Multipart message, From: Carl Worth cwo...@cworth.org, To: 
cwo...@cworth.org, Cc: , Bcc: , Date: Fri, 05 Jan 2001 15:43:57 
+}, body: [
 {id: 1, content-type: multipart/signed, content: [
 {id: 2, content-type: multipart/mixed, content: [
-{id: 3, content-type: message/rfc822, content: [{headers: {From: 
Carl Worth cwo...@cworth.org, To: cwo...@cworth.org, Subject: html 
message, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
+{id: 3, content-type: message/rfc822, content: [{headers: 
{Subject: html message, From: Carl Worth cwo...@cworth.org, To: 
cwo...@cworth.org, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
 {id: 4, content-type: multipart/alternative, content: [
 {id: 5, content-type: text/html}, 
 {id: 6, content-type: text/plain, content: This is an embedded 
message, with a multipart/alternative part.\n}]}]}]}, 
@@ -342,7 +342,7 @@ cat EOF EXPECTED
 
 {id: 1, content-type: multipart/signed, content: [
 {id: 2, content-type: multipart/mixed, content: [
-{id: 3, content-type: message/rfc822, content: [{headers: {From: 
Carl Worth cwo...@cworth.org, To: cwo...@cworth.org, Subject: html 
message, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
+{id: 3, content-type: message/rfc822, content: [{headers: 
{Subject: html message, From: Carl Worth cwo...@cworth.org, To: 
cwo...@cworth.org, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
 {id: 4, content-type: multipart/alternative, content: [
 {id: 5, content-type: text/html}, 
 {id: 6, content-type: text/plain, content: This is an embedded 
message, with a multipart/alternative part.\n}]}]}]}, 
@@ -358,7 +358,7 @@ echo OUTPUT # expect *no* newline at end of output
 cat EOF EXPECTED
 
 {id: 2, content-type: multipart/mixed, content: [
-{id: 3, content-type: message/rfc822, content: [{headers: {From: 
Carl Worth cwo...@cworth.org, To: cwo...@cworth.org, Subject: html 
message, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
+{id: 3, content-type: message/rfc822, content: [{headers: 
{Subject: html message, From: Carl Worth cwo...@cworth.org, To: 
cwo...@cworth.org, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
 {id: 4, content-type: multipart/alternative, content: [
 {id: 5, content-type: text/html}, 
 {id: 6, content-type: text/plain, content: This is an embedded 
message, with a multipart/alternative part.\n}]}]}]}, 
@@ -372,7 +372,7 @@ notmuch show --format=json --part=3 
'id:87liy5ap00@yoom.home.cworth.org' | s
 echo OUTPUT # expect *no* newline at end of output
 cat EOF EXPECTED
 
-{id: 3, content-type: message/rfc822, content: [{headers: {From: 
Carl Worth cwo...@cworth.org, To: cwo...@cworth.org, Subject: html 
message, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
+{id: 3, content-type: message/rfc822, content: [{headers: 
{Subject: html message, From: Carl Worth cwo...@cworth.org, To: 
cwo...@cworth.org, Date: Fri, 05 Jan 2001 15:42:57 +}, body: [
 {id: 4, content-type: multipart/alternative, content: [
 {id: 5, content-type: text/html}, 
 {id: 6, content-type: text/plain, content: This is an embedded 
message, with a multipart/alternative part.\n}]}]}]}
-- 
1.7.7.3

___
notmuch mailing list

[PATCH 2/8] show: Convert JSON format to the new self-recursive style

2012-02-14 Thread Austin Clements
As before, this is all code movement and a smidgen of glue.  This
moves the existing JSON formatter code into one self-recursive
function, but doesn't change any of the logic to take advantage of the
new structure.

In general, leafs of the JSON structure are left in helper functions
(most of them untouched), so that it's easy to see the overall
structure of the format from the main recursive function.
---
 notmuch-show.c |  273 
 1 files changed, 135 insertions(+), 138 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 93fb16f..868b2cd 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -35,52 +35,14 @@ static const notmuch_show_format_t format_text = {
 };
 
 static void
-format_message_json (const void *ctx,
-notmuch_message_t *message,
-unused (int indent));
-static void
-format_headers_json (const void *ctx,
-notmuch_message_t *message);
+format_part_json_entry (const void *ctx, mime_node_t *node,
+   int indent, const notmuch_show_params_t *params);
 
-static void
-format_headers_message_part_json (GMimeMessage *message);
-
-static void
-format_part_start_json (unused (GMimeObject *part),
-   int *part_count);
-
-static void
-format_part_encstatus_json (int status);
-
-static void
-#ifdef GMIME_ATLEAST_26
-format_part_sigstatus_json (GMimeSignatureList* siglist);
-#else
-format_part_sigstatus_json (const GMimeSignatureValidity* validity);
-#endif
-
-static void
-format_part_content_json (GMimeObject *part);
-
-static void
-format_part_end_json (GMimeObject *part);
-
-/* Any changes to the JSON format should be reflected in the file
- * devel/schemata. */
 static const notmuch_show_format_t format_json = {
-[, NULL,
-   {, format_message_json,
-   \headers\: {, format_headers_json, 
format_headers_message_part_json, },
-   , \body\: [,
-   format_part_start_json,
-   format_part_encstatus_json,
-   format_part_sigstatus_json,
-   format_part_content_json,
-   format_part_end_json,
-   , ,
-   ],
-   }, , ,
-]
+.message_set_start = [,
+.part = format_part_json_entry,
+.message_set_sep = , ,
+.message_set_end = ]
 };
 
 static void
@@ -170,7 +132,7 @@ _get_one_line_summary (const void *ctx, notmuch_message_t 
*message)
 }
 
 static void
-format_message_json (const void *ctx, notmuch_message_t *message, unused (int 
indent))
+format_message_json (const void *ctx, notmuch_message_t *message)
 {
 notmuch_tags_t *tags;
 int first = 1;
@@ -471,24 +433,6 @@ signer_status_to_string (GMimeSignerStatus x)
 }
 #endif
 
-static void
-format_part_start_json (unused (GMimeObject *part), int *part_count)
-{
-printf ({\id\: %d, *part_count);
-}
-
-static void
-format_part_encstatus_json (int status)
-{
-printf (, \encstatus\: [{\status\: );
-if (status) {
-   printf (\good\);
-} else {
-   printf (\bad\);
-}
-printf (}]);
-}
-
 #ifdef GMIME_ATLEAST_26
 static void
 format_part_sigstatus_json (GMimeSignatureList *siglist)
@@ -619,81 +563,6 @@ format_part_sigstatus_json (const GMimeSignatureValidity* 
validity)
 #endif
 
 static void
-format_part_content_json (GMimeObject *part)
-{
-GMimeContentType *content_type = g_mime_object_get_content_type 
(GMIME_OBJECT (part));
-GMimeStream *stream_memory = g_mime_stream_mem_new ();
-const char *cid = g_mime_object_get_content_id (part);
-void *ctx = talloc_new (NULL);
-GByteArray *part_content;
-
-printf (, \content-type\: %s,
-   json_quote_str (ctx, g_mime_content_type_to_string (content_type)));
-
-if (cid != NULL)
-   printf(, \content-id\: %s, json_quote_str (ctx, cid));
-
-if (GMIME_IS_PART (part))
-{
-   const char *filename = g_mime_part_get_filename (GMIME_PART (part));
-   if (filename)
-   printf (, \filename\: %s, json_quote_str (ctx, filename));
-}
-
-if (g_mime_content_type_is_type (content_type, text, *))
-{
-   /* For non-HTML text parts, we include the content in the
-* JSON. Since JSON must be Unicode, we handle charset
-* decoding here and do not report a charset to the caller.
-* For text/html parts, we do not include the content. If a
-* caller is interested in text/html parts, it should retrieve
-* them separately and they will not be decoded. Since this
-* makes charset decoding the responsibility on the caller, we
-* report the charset for text/html parts.
-*/
-   if (g_mime_content_type_is_type (content_type, text, html))
-   {
-   const char *content_charset = 
g_mime_object_get_content_type_parameter (GMIME_OBJECT (part), charset);
-
-   if (content_charset != NULL)
-   printf (, \content-charset\: %s, json_quote_str (ctx, 

[PATCH 5/8] show: Simplify talloc use in format_headers_json

2012-02-14 Thread Austin Clements
Previously there was an unnecessary talloc context.
---
 notmuch-show.c |   32 +++-
 1 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 209ff45..6259d30 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -291,37 +291,35 @@ format_headers_message_part_text (GMimeMessage *message)
 }
 
 static void
-format_headers_json (GMimeMessage *message)
+format_headers_json (const void *ctx, GMimeMessage *message)
 {
-void *ctx = talloc_new (NULL);
-void *ctx_quote = talloc_new (ctx);
+void *local = talloc_new (ctx);
 InternetAddressList *recipients;
 const char *recipients_string;
 
 printf (%s: %s,
-   json_quote_str (ctx_quote, Subject),
-   json_quote_str (ctx_quote, g_mime_message_get_subject (message)));
+   json_quote_str (local, Subject),
+   json_quote_str (local, g_mime_message_get_subject (message)));
 printf (, %s: %s,
-   json_quote_str (ctx_quote, From),
-   json_quote_str (ctx_quote, g_mime_message_get_sender (message)));
+   json_quote_str (local, From),
+   json_quote_str (local, g_mime_message_get_sender (message)));
 recipients = g_mime_message_get_recipients (message, 
GMIME_RECIPIENT_TYPE_TO);
 recipients_string = internet_address_list_to_string (recipients, 0);
 if (recipients_string)
printf (, %s: %s,
-   json_quote_str (ctx_quote, To),
-   json_quote_str (ctx_quote, recipients_string));
+   json_quote_str (local, To),
+   json_quote_str (local, recipients_string));
 recipients = g_mime_message_get_recipients (message, 
GMIME_RECIPIENT_TYPE_CC);
 recipients_string = internet_address_list_to_string (recipients, 0);
 if (recipients_string)
printf (, %s: %s,
-   json_quote_str (ctx_quote, Cc),
-   json_quote_str (ctx_quote, recipients_string));
+   json_quote_str (local, Cc),
+   json_quote_str (local, recipients_string));
 printf (, %s: %s,
-   json_quote_str (ctx_quote, Date),
-   json_quote_str (ctx_quote, g_mime_message_get_date_as_string 
(message)));
+   json_quote_str (local, Date),
+   json_quote_str (local, g_mime_message_get_date_as_string 
(message)));
 
-talloc_free (ctx_quote);
-talloc_free (ctx);
+talloc_free (local);
 }
 
 /* Write a MIME text part out to the given stream.
@@ -661,7 +659,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
format_message_json (ctx, node-envelope_file);
 
printf (\headers\: {);
-   format_headers_json (GMIME_MESSAGE (node-part));
+   format_headers_json (ctx, GMIME_MESSAGE (node-part));
printf (});
 
printf (, \body\: [);
@@ -749,7 +747,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 
 if (GMIME_IS_MESSAGE (node-part)) {
printf (\headers\: {);
-   format_headers_json (GMIME_MESSAGE (node-part));
+   format_headers_json (local, GMIME_MESSAGE (node-part));
printf (});
 
printf (, \body\: [);
-- 
1.7.7.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 6/8] show: Make JSON helper functions print complete objects

2012-02-14 Thread Austin Clements
This makes the main recursive function easier to follow because helper
functions don't add fields to the running object.
---
 notmuch-show.c |   15 +++
 1 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 6259d30..8fb6fa6 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -297,7 +297,7 @@ format_headers_json (const void *ctx, GMimeMessage *message)
 InternetAddressList *recipients;
 const char *recipients_string;
 
-printf (%s: %s,
+printf ({%s: %s,
json_quote_str (local, Subject),
json_quote_str (local, g_mime_message_get_subject (message)));
 printf (, %s: %s,
@@ -315,7 +315,7 @@ format_headers_json (const void *ctx, GMimeMessage *message)
printf (, %s: %s,
json_quote_str (local, Cc),
json_quote_str (local, recipients_string));
-printf (, %s: %s,
+printf (, %s: %s},
json_quote_str (local, Date),
json_quote_str (local, g_mime_message_get_date_as_string 
(message)));
 
@@ -406,7 +406,7 @@ signer_status_to_string (GMimeSignerStatus x)
 static void
 format_part_sigstatus_json (GMimeSignatureList *siglist)
 {
-printf (, \sigstatus\: [);
+printf ([);
 
 if (!siglist) {
printf (]);
@@ -472,7 +472,7 @@ format_part_sigstatus_json (GMimeSignatureList *siglist)
 static void
 format_part_sigstatus_json (const GMimeSignatureValidity* validity)
 {
-printf (, \sigstatus\: [);
+printf ([);
 
 if (!validity) {
printf (]);
@@ -658,9 +658,8 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
printf ({);
format_message_json (ctx, node-envelope_file);
 
-   printf (\headers\: {);
+   printf (\headers\: );
format_headers_json (ctx, GMIME_MESSAGE (node-part));
-   printf (});
 
printf (, \body\: [);
format_part_json (ctx, mime_node_child (node, 0), first);
@@ -696,6 +695,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 }
 
 if (node-verify_attempted) {
+   printf (, \sigstatus\: );
 #ifdef GMIME_ATLEAST_26
format_part_sigstatus_json (node-sig_list);
 #else
@@ -746,9 +746,8 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
g_object_unref (stream_memory);
 
 if (GMIME_IS_MESSAGE (node-part)) {
-   printf (\headers\: {);
+   printf (\headers\: );
format_headers_json (local, GMIME_MESSAGE (node-part));
-   printf (});
 
printf (, \body\: [);
 }
-- 
1.7.7.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 4/8] show: Unify JSON header output for messages and message parts

2012-02-14 Thread Austin Clements
This has three ramifications:
- Blank To and Cc headers are no longer output for messages.
- Dates are now canonicalized for messages, which means they always
  have a day of the week and GMT is printed + (never -)
- Invalid From message headers are handled slightly differently, since
  they get parsed by GMime now instead of notmuch.
---
 notmuch-show.c|   35 +++
 test/crypto   |   35 ++-
 test/emacs|4 ++--
 test/json |6 +++---
 test/maildir-sync |2 --
 test/multipart|2 +-
 6 files changed, 23 insertions(+), 61 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 9ca9882..209ff45 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -291,36 +291,7 @@ format_headers_message_part_text (GMimeMessage *message)
 }
 
 static void
-format_headers_json (const void *ctx, notmuch_message_t *message)
-{
-const char *headers[] = {
-   Subject, From, To, Cc, Bcc, Date
-};
-const char *name, *value;
-unsigned int i;
-int first_header = 1;
-void *ctx_quote = talloc_new (ctx);
-
-for (i = 0; i  ARRAY_SIZE (headers); i++) {
-   name = headers[i];
-   value = notmuch_message_get_header (message, name);
-   if (value)
-   {
-   if (!first_header)
-   fputs (, , stdout);
-   first_header = 0;
-
-   printf (%s: %s,
-   json_quote_str (ctx_quote, name),
-   json_quote_str (ctx_quote, value));
-   }
-}
-
-talloc_free (ctx_quote);
-}
-
-static void
-format_headers_message_part_json (GMimeMessage *message)
+format_headers_json (GMimeMessage *message)
 {
 void *ctx = talloc_new (NULL);
 void *ctx_quote = talloc_new (ctx);
@@ -690,7 +661,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
format_message_json (ctx, node-envelope_file);
 
printf (\headers\: {);
-   format_headers_json (ctx, node-envelope_file);
+   format_headers_json (GMIME_MESSAGE (node-part));
printf (});
 
printf (, \body\: [);
@@ -778,7 +749,7 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 
 if (GMIME_IS_MESSAGE (node-part)) {
printf (\headers\: {);
-   format_headers_message_part_json (GMIME_MESSAGE (node-part));
+   format_headers_json (GMIME_MESSAGE (node-part));
printf (});
 
printf (, \body\: [);
diff --git a/test/crypto b/test/crypto
index 1dbb60a..7e774c8 100755
--- a/test/crypto
+++ b/test/crypto
@@ -50,9 +50,8 @@ expected='[[[{id: X,
  headers: {Subject: test signed message 001,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 12:00:00 +},
  body: [{id: 1,
  sigstatus: [{status: good,
  fingerprint: '$FINGERPRINT',
@@ -84,9 +83,8 @@ expected='[[[{id: X,
  headers: {Subject: test signed message 001,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 12:00:00 +},
  body: [{id: 1,
  sigstatus: [{status: good,
  fingerprint: '$FINGERPRINT',
@@ -120,9 +118,8 @@ expected='[[[{id: X,
  headers: {Subject: test signed message 001,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 12:00:00 +},
  body: [{id: 1,
  sigstatus: [{status: error,
  keyid: '$(echo $FINGERPRINT | cut -c 25-)',
@@ -194,9 +191,8 @@ expected='[[[{id: X,
  headers: {Subject: test encrypted message 001,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 12:00:00 +},
  body: [{id: 1,
  encstatus: [{status: good}],
  sigstatus: [],
@@ -249,9 +245,8 @@ expected='[[[{id: X,
  headers: {Subject: test encrypted message 001,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 12:00:00 +},
  body: [{id: 1,
  encstatus: [{status: bad}],
  content-type: multipart/encrypted,
@@ -284,9 +279,8 @@ expected='[[[{id: X,
  headers: {Subject: test encrypted message 002,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 12:00:00 +},
  body: [{id: 1,
  encstatus: [{status: good}],
  sigstatus: [{status: good,
@@ -339,9 +333,8 @@ expected='[[[{id: X,
  headers: {Subject: test signed message 001,
  From: Notmuch Test Suite test_su...@notmuchmail.org,
  To: test_su...@notmuchmail.org,
- Cc: ,
- Bcc: ,
- Date: 01 Jan 2000 12:00:00 -},
+ Date: Sat,
+ 01 Jan 2000 

[PATCH 8/8] show: Further general simplifications of the JSON formatter

2012-02-14 Thread Austin Clements
---
 notmuch-show.c |   61 ---
 1 files changed, 22 insertions(+), 39 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 07276c7..6a171a4 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -678,9 +678,10 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 GMimeObject *meta = node-envelope_part ?
GMIME_OBJECT (node-envelope_part) : node-part;
 GMimeContentType *content_type = g_mime_object_get_content_type (meta);
-GMimeStream *stream_memory = g_mime_stream_mem_new ();
 const char *cid = g_mime_object_get_content_id (meta);
-GByteArray *part_content;
+const char *filename = GMIME_IS_PART (node-part) ?
+   g_mime_part_get_filename (GMIME_PART (node-part)) : NULL;
+const char *terminator = ;
 int i;
 
 if (!first)
@@ -688,15 +689,9 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 
 printf ({\id\: %d, node-part_num);
 
-if (node-decrypt_attempted) {
-   printf (, \encstatus\: [{\status\: );
-   if (node-decrypt_success) {
-   printf (\good\);
-   } else {
-   printf (\bad\);
-   }
-   printf (}]);
-}
+if (node-decrypt_attempted)
+   printf (, \encstatus\: [{\status\: \%s\}],
+   node-decrypt_success ? good : bad);
 
 if (node-verify_attempted) {
printf (, \sigstatus\: );
@@ -706,16 +701,13 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 printf (, \content-type\: %s,
json_quote_str (local, g_mime_content_type_to_string 
(content_type)));
 
-if (cid != NULL)
-   printf(, \content-id\: %s, json_quote_str (local, cid));
+if (cid)
+   printf (, \content-id\: %s, json_quote_str (local, cid));
+
+if (filename)
+   printf (, \filename\: %s, json_quote_str (local, filename));
 
 if (GMIME_IS_PART (node-part)) {
-   const char *filename = g_mime_part_get_filename (GMIME_PART 
(node-part));
-   if (filename)
-   printf (, \filename\: %s, json_quote_str (local, filename));
-}
-
-if (g_mime_content_type_is_type (content_type, text, *)) {
/* For non-HTML text parts, we include the content in the
 * JSON. Since JSON must be Unicode, we handle charset
 * decoding here and do not report a charset to the caller.
@@ -730,42 +722,33 @@ format_part_json (const void *ctx, mime_node_t *node, 
notmuch_bool_t first)
 
if (content_charset != NULL)
printf (, \content-charset\: %s, json_quote_str (local, 
content_charset));
-   } else {
+   } else if (g_mime_content_type_is_type (content_type, text, *)) {
+   GMimeStream *stream_memory = g_mime_stream_mem_new ();
+   GByteArray *part_content;
show_text_part_content (node-part, stream_memory);
part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM 
(stream_memory));
 
printf (, \content\: %s, json_quote_chararray (local, (char *) 
part_content-data, part_content-len));
+   g_object_unref (stream_memory);
}
-} else if (g_mime_content_type_is_type (content_type, multipart, *)) {
+} else if (GMIME_IS_MULTIPART (node-part)) {
printf (, \content\: [);
-} else if (g_mime_content_type_is_type (content_type, message, 
rfc822)) {
+   terminator = ];
+} else if (GMIME_IS_MESSAGE (node-part)) {
printf (, \content\: [{);
-}
-
-if (stream_memory)
-   g_object_unref (stream_memory);
-
-if (GMIME_IS_MESSAGE (node-part)) {
printf (\headers\: );
format_headers_json (local, GMIME_MESSAGE (node-part));
 
printf (, \body\: [);
+   terminator = ]}];
 }
 
+talloc_free (local);
+
 for (i = 0; i  node-nchildren; i++)
format_part_json (ctx, mime_node_child (node, i), i == 0);
 
-if (GMIME_IS_MESSAGE (node-part))
-   printf (]);
-
-if (g_mime_content_type_is_type (content_type, multipart, *))
-   printf (]);
-else if (g_mime_content_type_is_type (content_type, message, rfc822))
-   printf (}]);
-
-printf (});
-
-talloc_free (local);
+printf (%s}, terminator);
 }
 
 static void
-- 
1.7.7.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v6 1/3] test: add tests wrt ignoring user-specified files and directories

2012-02-14 Thread Austin Clements
On Mon,  6 Feb 2012 11:28:23 +0200, Tomi Ollila tomi.oll...@iki.fi wrote:
 Files and directories which are specified in 'new.ignore' in the
 config file shouldn't be indexed nor reported by `notmuch new'.
 
 This is basically Pieter's work with Austin's comments addressed.
 ---
  test/new |   22 ++
  1 files changed, 22 insertions(+), 0 deletions(-)
 
 diff --git a/test/new b/test/new
 index 49f390d..5ce8811 100755
 --- a/test/new
 +++ b/test/new
 @@ -153,4 +153,26 @@ rm -rf ${MAIL_DIR}/two
  output=$(NOTMUCH_NEW)
  test_expect_equal $output No new mail. Removed 3 messages.
  
 +# This test depends that notmuch new has been run at least once.

s/depends/requires/

 +test_begin_subtest Skip and report non-mail files
 +generate_message
 +mkdir -p ${MAIL_DIR}/.git  touch ${MAIL_DIR}/.git/config
 +touch ${MAIL_DIR}/ignored_file
 +touch ${MAIL_DIR}/.ignored_hidden_file
 +output=$(NOTMUCH_NEW 21)
 +test_expect_equal $output \
 +Note: Ignoring non-mail file: ${MAIL_DIR}/.git/config
 +Note: Ignoring non-mail file: ${MAIL_DIR}/.ignored_hidden_file
 +Note: Ignoring non-mail file: ${MAIL_DIR}/ignored_file
 +Added 1 new message to the database.
 +
 +test_begin_subtest Ignore files and directories specified in new.ignore
 +test_subtest_known_broken
 +generate_message
 +notmuch config set new.ignore .git ignored_file .ignored_hidden_file
 +touch ${MAIL_DIR}/.git # change .git's mtime for notmuch new to rescan.
 +output=$(NOTMUCH_NEW 21)
 +test_expect_equal $output Added 1 new message to the database.
 +
 +
  test_done
 -- 
 1.7.6.5
 
 ___
 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 v6 3/3] NEWS: add news section for new.ignore

2012-02-14 Thread Austin Clements
On Mon,  6 Feb 2012 11:28:25 +0200, Tomi Ollila tomi.oll...@iki.fi wrote:
 Added NEWS section 'Mail store folder/file ignore'.
 ---
  NEWS |6 ++
  1 files changed, 6 insertions(+), 0 deletions(-)
 
 diff --git a/NEWS b/NEWS
 index 5c5b645..59da584 100644
 --- a/NEWS
 +++ b/NEWS
 @@ -29,6 +29,12 @@ Tag exclusion
  
  notmuch config set search.exclude_tags deleted spam
  
 +Mail store folder/file ignore
 +
 +  New configuration option `new.ignore` is used to inform notmuch about
 +  folders and files that are not mail files or contain material that is
 +  not desired to be known by notmuch.
 +

This is a bit unwieldy.  How about something based on the config file
comment?

  A new configuration option, `new.ignore`, lets users specify a
  ;-separated list of file and directory names that will not be searched
  for messages by notmuch new.

Plus, this way it's not so bad that we can't upgrade an existing config
file comment.

  Emacs Interface
  ---
  
 -- 
 1.7.6.5
 
 ___
 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 v6 1/3] test: add tests wrt ignoring user-specified files and directories

2012-02-14 Thread Austin Clements
On Mon, 13 Feb 2012 10:20:28 +0200, Tomi Ollila tomi.oll...@iki.fi wrote:
 Those interested in this patch set please check lates changes.
 The test patch (1/3) has been slighty modified from Pieter's
 version and NEWS patch (3/3) is new. The actual functionality
 in patch 2/3 is exactly the same as in previous version.
 
 This applies cleanly in top of current master
 ( a5674c21584a32a4f8a8a3e9ea3c3576f772e744 ) pushed Sun 2012-02-12.

This series LGTM other than the two minor wording changes I just sent
out.
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: notmuch-emacs bug report -- infinite looping trying to select next message

2012-02-14 Thread Michael Hudson-Doyle
Thanks for the reply!

On Tue, 14 Feb 2012 10:41:20 +0100, Rodney Lorrimar d...@rodney.id.au wrote:
 Hi Michael,
 
 On Tue, 14 Feb 2012 11:01:56 +1300, Michael Hudson-Doyle 
 michael.hud...@canonical.com wrote:
  The attached gzipped mbox appears to trip up the emacs interface.  The
  problem seems to come from the message with id
  CAGNsrLCWv6=36q+q+5Hc_SzgdZ2ergeKkapT7T3xXvim=2c...@mail.gmail.com.
  
  If you load up the thread in emacs, you get a message:
  
  mm-extern-cache-contents: Couldn't find access type
 
 If you put (require 'gnus-art) into your .emacs and eval it, does the
 problem go away?

No.

 I had a similar problem when running a newer emacs-snapshot with
 notmuch. See this thread: id:87d3aaqyur@eve.chaoflow.net

I'm running emacs-snapshot-gtk from Ubuntu Oneiric, emacs-version says

GNU Emacs 23.3.1 (x86_64-pc-linux-gnu, GTK+ Version 2.24.5) of 2011-08-15 on 
allspice, modified by Debian

  Then attempting to advance past the last display message (or pressing A,
  or a few other things I expect gets emacs to loop indefinitely.
  toggle-debug-on-quit gets me this backtrace:
 
 I believe the actual bug is in gnus but I don't really like the loop on
 error behaviour of notmuch. I would like to try and fix it but haven't
 found the time.

As far as I can tell, gnus isn't involved here.  I may be wrong, of
course!

Cheers,
mwh
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Free the results of scandir()

2012-02-14 Thread David Bremner
On Tue,  7 Feb 2012 05:05:03 -0500, Ethan Glasser-Camp gla...@cs.rpi.edu 
wrote:
 From: Ethan Glasser-Camp et...@betacantrips.com
 
 scandir() returns strings allocated via malloc(3) which are then
 collected in array namelist which is allocated via
 malloc(3). Currently we just free the array namelist. Instead, free
 all the entries of namelist, and then free namelist.

pushed, thanks

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] emacs: allow to set RETAIN-STATE for `notmuch-show-refresh-view' interactively

2012-02-14 Thread David Bremner
On Mon, 13 Feb 2012 15:09:07 +0400, Dmitry Kurochkin 
dmitry.kuroch...@gmail.com wrote:
 The notmuch-show view refresh function (`notmuch-show-refresh-view',
 bound to =) accepts an optional RETAIN-STATE argument.  The patch
 allows to set this argument interactively by using C-u =.

pushed

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 0/4] Reply enhancements (was: quoting HTML email ...)

2012-02-14 Thread Adam Wolfe Gordon
Hi everyone,

There are relatively few changes from the last version [1], but the JSON
format has big changes again. A summary of all the changes:

* The JSON reply format now uses the new formatter from the show JSON
  format. This means that the MUA will not need to call notmuch show
  for text/* parts, except HTML, as the content of those parts will be
  included in the JSON output.

* The JSON reply format has changed in a few other minor ways, due to
  reusing the show code.

* Because the original message is now included in the reply format, the
  old show behavior of --format=json implies --entire-thread is reinstated.
  I still think this is a weird behavior, but there's no good reason to
  change it.

* The emacs code is simplified a bit by the changes to the JSON format, but
  its behavior is basically the same.

* Man pages and tests changed to reflect the above changes.

Note that, as implied above, this series relies on Austin's rewrite of the
show JSON formatter [2]. I should probably add the reply JSON format to
the devel/schemata file, but I'll leave that for another patch (perhaps
in a series with a news entry if this is deemed ready to push).

Thanks to everyone for the replies on previous versions, and please let
me know if you have any comments. Also, it would be great if others can
test this out on some interesting messages, as my email collection is
not very esoteric.

[1] id:1328746916-25447-1-git-send-email-awg+notm...@xvx.ca
[2] id:1329240823-7856-1-git-send-email-amdra...@mit.edu

Adam Wolfe Gordon (4):
  test: Add broken test for the new JSON reply format.
  reply: Add a JSON reply format.
  man: Update notmuch-reply man page for JSON format.
  emacs: Use the new JSON reply format and message-cite-original

 emacs/notmuch-lib.el |   39 +++
 emacs/notmuch-mua.el |  123 +--
 emacs/notmuch-show.el|   24 +--
 man/man1/notmuch-reply.1 |5 ++
 notmuch-client.h |3 +
 notmuch-reply.c  |  164 ++
 notmuch-show.c   |2 +-
 test/emacs   |  101 -
 test/multipart   |   51 
+++
 9 files changed, 365 insertions(+), 103 deletions(-)

-- 
1.7.5.4

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 1/4] test: Add broken test for the new JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
---
 test/multipart |   51 +++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/test/multipart b/test/multipart
index a3036b4..9651568 100755
--- a/test/multipart
+++ b/test/multipart
@@ -589,6 +589,57 @@ Non-text part: text/html
 EOF
 test_expect_equal_file OUTPUT EXPECTED
 
+test_begin_subtest 'notmuch reply' to a multipart message with json format
+notmuch reply --format=json 'id:87liy5ap00@yoom.home.cworth.org' | 
notmuch_json_show_sanitize OUTPUT
+cat EOF EXPECTED
+{reply-headers: {from: Notmuch Test Suite test_su...@notmuchmail.org,
+ to: Carl Worth cwo...@cworth.org,
+ cwo...@cworth.org,
+ subject: Re: Multipart message,
+ in-reply-to: 87liy5ap00@yoom.home.cworth.org,
+ references:  87liy5ap00@yoom.home.cworth.org},
+ original: {id: X,
+ match: false,
+ filename: Y,
+ timestamp: 978709437,
+ date_relative: 2001-01-05,
+ tags: [attachment,inbox,signed,unread],
+ headers: {Subject: Multipart message,
+ From: Carl Worth cwo...@cworth.org,
+ To: cwo...@cworth.org,
+ Date: Fri,
+ 05 Jan 2001 15:43:57 +},
+ body: [{id: 1,
+ content-type: multipart/signed,
+ content: [{id: 2,
+ content-type: multipart/mixed,
+ content: [{id: 3,
+ content-type: message/rfc822,
+ content: [{headers: {Subject: html message,
+ From: Carl Worth cwo...@cworth.org,
+ To: cwo...@cworth.org,
+ Date: Fri,
+ 05 Jan 2001 15:42:57 +},
+ body: [{id: 4,
+ content-type: multipart/alternative,
+ content: [{id: 5,
+ content-type: text/html},
+ {id: 6,
+ content-type: text/plain,
+ content: This is an embedded message,
+ with a multipart/alternative part.\n}]}]}]},
+ {id: 7,
+ content-type: text/plain,
+ filename: Y,
+ content: This is a text attachment.\n},
+ {id: 8,
+ content-type: text/plain,
+ content: And this message is signed.\n\n-Carl\n}]},
+ {id: 9,
+ content-type: application/pgp-signature}]}]}}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
 test_begin_subtest 'notmuch show --part' does not corrupt a part with CRLF 
pair
 notmuch show --format=raw --part=3 id:base64-part-with-crlf  crlf.out
 echo -n -e \xEF\x0D\x0A  crlf.expected
-- 
1.7.5.4

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 2/4] reply: Add a JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
This new JSON format for replies includes headers generated for a
reply message as well as the headers of the original message.  Using
this data, a client can intelligently create a reply. For example, the
emacs client will be able to create replies with quoted HTML parts by
parsing the HTML parts.

Reply now enforces that only one message is returned, as the semantics
of replying to multiple messages are not well-defined.
---
 notmuch-client.h |3 +
 notmuch-reply.c  |  164 --
 notmuch-show.c   |2 +-
 3 files changed, 125 insertions(+), 44 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 60828aa..d28ea07 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -344,6 +344,9 @@ typedef struct mime_node {
 int next_part_num;
 } mime_node_t;
 
+void
+format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);
+
 /* Construct a new MIME node pointing to the root message part of
  * message.  If cryptoctx is non-NULL, it will be used to verify
  * signatures on any child parts.  If decrypt is true, then cryptoctx
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 6b244e6..979ad86 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -505,6 +505,61 @@ guess_from_received_header (notmuch_config_t *config, 
notmuch_message_t *message
 return NULL;
 }
 
+static GMimeMessage *
+create_reply_message(void *ctx,
+notmuch_config_t *config,
+notmuch_message_t *message,
+notmuch_bool_t reply_all)
+{
+const char *subject, *from_addr = NULL;
+const char *in_reply_to, *orig_references, *references;
+
+/* The 1 means we want headers in a pretty order. */
+GMimeMessage *reply = g_mime_message_new (1);
+if (reply == NULL) {
+   fprintf (stderr, Out of memory\n);
+   return NULL;
+}
+
+subject = notmuch_message_get_header (message, subject);
+if (subject) {
+   if (strncasecmp (subject, Re:, 3))
+   subject = talloc_asprintf (ctx, Re: %s, subject);
+   g_mime_message_set_subject (reply, subject);
+}
+
+from_addr = add_recipients_from_message (reply, config,
+message, reply_all);
+
+if (from_addr == NULL)
+   from_addr = guess_from_received_header (config, message);
+
+if (from_addr == NULL)
+   from_addr = notmuch_config_get_user_primary_email (config);
+
+from_addr = talloc_asprintf (ctx, %s %s,
+notmuch_config_get_user_name (config),
+from_addr);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ From, from_addr);
+
+in_reply_to = talloc_asprintf (ctx, %s,
+  notmuch_message_get_message_id (message));
+
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ In-Reply-To, in_reply_to);
+
+orig_references = notmuch_message_get_header (message, references);
+references = talloc_asprintf (ctx, %s%s%s,
+ orig_references ? orig_references : ,
+ orig_references ?   : ,
+ in_reply_to);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ References, references);
+
+return reply;
+}
+
 static int
 notmuch_reply_format_default(void *ctx,
 notmuch_config_t *config,
@@ -515,8 +570,6 @@ notmuch_reply_format_default(void *ctx,
 GMimeMessage *reply;
 notmuch_messages_t *messages;
 notmuch_message_t *message;
-const char *subject, *from_addr = NULL;
-const char *in_reply_to, *orig_references, *references;
 const notmuch_show_format_t *format = format_reply;
 
 for (messages = notmuch_query_search_messages (query);
@@ -525,48 +578,10 @@ notmuch_reply_format_default(void *ctx,
 {
message = notmuch_messages_get (messages);
 
-   /* The 1 means we want headers in a pretty order. */
-   reply = g_mime_message_new (1);
-   if (reply == NULL) {
-   fprintf (stderr, Out of memory\n);
-   return 1;
-   }
-
-   subject = notmuch_message_get_header (message, subject);
-   if (subject) {
-   if (strncasecmp (subject, Re:, 3))
-   subject = talloc_asprintf (ctx, Re: %s, subject);
-   g_mime_message_set_subject (reply, subject);
-   }
-
-   from_addr = add_recipients_from_message (reply, config, message,
-reply_all);
-
-   if (from_addr == NULL)
-   from_addr = guess_from_received_header (config, message);
-
-   if (from_addr == NULL)
-   from_addr = notmuch_config_get_user_primary_email (config);
-
-   from_addr = talloc_asprintf (ctx, %s %s,
-notmuch_config_get_user_name (config),
-

[PATCH v5 4/4] emacs: Use the new JSON reply format and message-cite-original

2012-02-14 Thread Adam Wolfe Gordon
Using the new JSON reply format allows emacs to quote HTML parts
nicely by using mm-display-part to turn them into displayable text,
then quoting them with message-cite-original. This is very useful for
users who regularly receive HTML-only email.

Use message-mode's message-cite-original function to create the
quoted body for reply messages. In order to make this act like the
existing notmuch defaults, you will need to set the following in
your emacs configuration:

message-citation-line-format On %a, %d %b %Y, %f wrote:
message-citation-line-function 'message-insert-formatted-citation-line

The test has been updated to reflect the (ugly) emacs default.
---
 emacs/notmuch-lib.el  |   39 +++
 emacs/notmuch-mua.el  |  123 +++--
 emacs/notmuch-show.el |   24 +-
 test/emacs|  101 +++-
 4 files changed, 228 insertions(+), 59 deletions(-)

diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
index d315f76..3fc7aff 100644
--- a/emacs/notmuch-lib.el
+++ b/emacs/notmuch-lib.el
@@ -21,6 +21,8 @@
 
 ;; This is an part of an emacs-based interface to the notmuch mail system.
 
+(eval-when-compile (require 'cl))
+
 (defvar notmuch-command notmuch
   Command to run the notmuch binary.)
 
@@ -173,6 +175,43 @@ the user hasn't set this variable with the old or new 
value.
   (list 'when ( emacs-major-version 23)
form))
 
+(defun notmuch-split-content-type (content-type)
+  Split content/type into 'content' and 'type'
+  (split-string content-type /))
+
+(defun notmuch-match-content-type (t1 t2)
+  Return t if t1 and t2 are matching content types, taking wildcards into 
account
+  (let ((st1 (notmuch-split-content-type t1))
+   (st2 (notmuch-split-content-type t2)))
+(if (or (string= (cadr st1) *)
+   (string= (cadr st2) *))
+   (string= (car st1) (car st2))
+  (string= t1 t2
+
+(defvar notmuch-multipart/alternative-discouraged
+  '(
+;; Avoid HTML parts.
+text/html
+;; multipart/related usually contain a text/html part and some associated 
graphics.
+multipart/related
+))
+
+(defun notmuch-multipart/alternative-choose (types)
+  Return a list of preferred types from the given list of types
+  ;; Based on `mm-preferred-alternative-precedence'.
+  (let ((seq types))
+(dolist (pref (reverse notmuch-multipart/alternative-discouraged))
+  (dolist (elem (copy-sequence seq))
+   (when (string-match pref elem)
+ (setq seq (nconc (delete elem seq) (list elem))
+seq))
+
+(defun notmuch-parts-filter-by-type (parts type)
+  Given a vector of message parts, return a vector containing the ones 
matching the given type.
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) type)
+   vconcat (list part)))
+
 ;; Compatibility functions for versions of emacs before emacs 23.
 ;;
 ;; Both functions here were copied from emacs 23 with the following copyright:
diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 4be7c13..371993f 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -19,11 +19,15 @@
 ;;
 ;; Authors: David Edmondson d...@dme.org
 
+(require 'json)
 (require 'message)
+(require 'format-spec)
 
 (require 'notmuch-lib)
 (require 'notmuch-address)
 
+(eval-when-compile (require 'cl))
+
 ;;
 
 (defcustom notmuch-mua-send-hook '(notmuch-mua-message-send-hook)
@@ -72,56 +76,105 @@ list.
(push header message-hidden-headers)))
notmuch-mua-hidden-headers))
 
+(defun notmuch-mua-get-displayed-part (part query-string)
+  (with-temp-buffer
+(if (assq 'content part)
+   (insert (cdr (assq 'content part)))
+  (call-process notmuch-command nil t nil show --format=raw
+   (format --part=%s (cdr (assq 'id part)))
+   query-string))
+
+(let ((handle (mm-make-handle (current-buffer) (list (cdr (assq 
'content-type part)
+ (end-of-orig (point-max)))
+  (mm-display-part handle)
+  (delete-region (point-min) end-of-orig)
+  (buffer-substring (point-min) (point-max)
+
+(defun notmuch-mua-multipart/*-to-list (parts)
+  (loop for part across parts
+   collect (cdr (assq 'content-type part
+
+(defun notmuch-mua-get-quotable-parts (parts)
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) 
multipart/alternative)
+ append (let* ((subparts (cdr (assq 'content part)))
+   (types (notmuch-mua-multipart/*-to-list subparts))
+   (chosen-type (car (notmuch-multipart/alternative-choose 
types
+  (notmuch-mua-get-quotable-parts 
(notmuch-parts-filter-by-type subparts chosen-type)))
+   else if (notmuch-match-content-type (cdr (assq 'content-type part)) 
multipart/*)
+ append (notmuch-mua-get-quotable-parts (cdr (assq 'content part)))
+   else if 

[PATCH v5.1 1/4] test: Add broken test for the new JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
---

Adjusted the header display to be consistent with the other JSON formats.

 test/multipart |   51 +++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/test/multipart b/test/multipart
index a3036b4..e7abcc2 100755
--- a/test/multipart
+++ b/test/multipart
@@ -589,6 +589,57 @@ Non-text part: text/html
 EOF
 test_expect_equal_file OUTPUT EXPECTED
 
+test_begin_subtest 'notmuch reply' to a multipart message with json format
+notmuch reply --format=json 'id:87liy5ap00@yoom.home.cworth.org' | 
notmuch_json_show_sanitize OUTPUT
+cat EOF EXPECTED
+{reply-headers: {Subject: Re: Multipart message,
+ From: Notmuch Test Suite test_su...@notmuchmail.org,
+ To: Carl Worth cwo...@cworth.org,
+ cwo...@cworth.org,
+ In-reply-to: 87liy5ap00@yoom.home.cworth.org,
+ References:  87liy5ap00@yoom.home.cworth.org},
+ original: {id: X,
+ match: false,
+ filename: Y,
+ timestamp: 978709437,
+ date_relative: 2001-01-05,
+ tags: [attachment,inbox,signed,unread],
+ headers: {Subject: Multipart message,
+ From: Carl Worth cwo...@cworth.org,
+ To: cwo...@cworth.org,
+ Date: Fri,
+ 05 Jan 2001 15:43:57 +},
+ body: [{id: 1,
+ content-type: multipart/signed,
+ content: [{id: 2,
+ content-type: multipart/mixed,
+ content: [{id: 3,
+ content-type: message/rfc822,
+ content: [{headers: {Subject: html message,
+ From: Carl Worth cwo...@cworth.org,
+ To: cwo...@cworth.org,
+ Date: Fri,
+ 05 Jan 2001 15:42:57 +},
+ body: [{id: 4,
+ content-type: multipart/alternative,
+ content: [{id: 5,
+ content-type: text/html},
+ {id: 6,
+ content-type: text/plain,
+ content: This is an embedded message,
+ with a multipart/alternative part.\n}]}]}]},
+ {id: 7,
+ content-type: text/plain,
+ filename: Y,
+ content: This is a text attachment.\n},
+ {id: 8,
+ content-type: text/plain,
+ content: And this message is signed.\n\n-Carl\n}]},
+ {id: 9,
+ content-type: application/pgp-signature}]}]}}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
 test_begin_subtest 'notmuch show --part' does not corrupt a part with CRLF 
pair
 notmuch show --format=raw --part=3 id:base64-part-with-crlf  crlf.out
 echo -n -e \xEF\x0D\x0A  crlf.expected
-- 
1.7.5.4

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5.1 2/4] reply: Add a JSON reply format.

2012-02-14 Thread Adam Wolfe Gordon
This new JSON format for replies includes headers generated for a
reply message as well as the headers of the original message.  Using
this data, a client can intelligently create a reply. For example, the
emacs client will be able to create replies with quoted HTML parts by
parsing the HTML parts.

Reply now enforces that only one message is returned, as the semantics
of replying to multiple messages are not well-defined.
---

Adjusted the header display to be consistent with the other JSON formats.

 notmuch-client.h |3 +
 notmuch-reply.c  |  188 +
 notmuch-show.c   |2 +-
 3 files changed, 149 insertions(+), 44 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 60828aa..d28ea07 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -344,6 +344,9 @@ typedef struct mime_node {
 int next_part_num;
 } mime_node_t;
 
+void
+format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);
+
 /* Construct a new MIME node pointing to the root message part of
  * message.  If cryptoctx is non-NULL, it will be used to verify
  * signatures on any child parts.  If decrypt is true, then cryptoctx
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 6b244e6..76995b1 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -505,6 +505,61 @@ guess_from_received_header (notmuch_config_t *config, 
notmuch_message_t *message
 return NULL;
 }
 
+static GMimeMessage *
+create_reply_message(void *ctx,
+notmuch_config_t *config,
+notmuch_message_t *message,
+notmuch_bool_t reply_all)
+{
+const char *subject, *from_addr = NULL;
+const char *in_reply_to, *orig_references, *references;
+
+/* The 1 means we want headers in a pretty order. */
+GMimeMessage *reply = g_mime_message_new (1);
+if (reply == NULL) {
+   fprintf (stderr, Out of memory\n);
+   return NULL;
+}
+
+subject = notmuch_message_get_header (message, subject);
+if (subject) {
+   if (strncasecmp (subject, Re:, 3))
+   subject = talloc_asprintf (ctx, Re: %s, subject);
+   g_mime_message_set_subject (reply, subject);
+}
+
+from_addr = add_recipients_from_message (reply, config,
+message, reply_all);
+
+if (from_addr == NULL)
+   from_addr = guess_from_received_header (config, message);
+
+if (from_addr == NULL)
+   from_addr = notmuch_config_get_user_primary_email (config);
+
+from_addr = talloc_asprintf (ctx, %s %s,
+notmuch_config_get_user_name (config),
+from_addr);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ From, from_addr);
+
+in_reply_to = talloc_asprintf (ctx, %s,
+  notmuch_message_get_message_id (message));
+
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ In-Reply-To, in_reply_to);
+
+orig_references = notmuch_message_get_header (message, references);
+references = talloc_asprintf (ctx, %s%s%s,
+ orig_references ? orig_references : ,
+ orig_references ?   : ,
+ in_reply_to);
+g_mime_object_set_header (GMIME_OBJECT (reply),
+ References, references);
+
+return reply;
+}
+
 static int
 notmuch_reply_format_default(void *ctx,
 notmuch_config_t *config,
@@ -515,8 +570,6 @@ notmuch_reply_format_default(void *ctx,
 GMimeMessage *reply;
 notmuch_messages_t *messages;
 notmuch_message_t *message;
-const char *subject, *from_addr = NULL;
-const char *in_reply_to, *orig_references, *references;
 const notmuch_show_format_t *format = format_reply;
 
 for (messages = notmuch_query_search_messages (query);
@@ -525,48 +578,10 @@ notmuch_reply_format_default(void *ctx,
 {
message = notmuch_messages_get (messages);
 
-   /* The 1 means we want headers in a pretty order. */
-   reply = g_mime_message_new (1);
-   if (reply == NULL) {
-   fprintf (stderr, Out of memory\n);
-   return 1;
-   }
-
-   subject = notmuch_message_get_header (message, subject);
-   if (subject) {
-   if (strncasecmp (subject, Re:, 3))
-   subject = talloc_asprintf (ctx, Re: %s, subject);
-   g_mime_message_set_subject (reply, subject);
-   }
-
-   from_addr = add_recipients_from_message (reply, config, message,
-reply_all);
-
-   if (from_addr == NULL)
-   from_addr = guess_from_received_header (config, message);
-
-   if (from_addr == NULL)
-   from_addr = notmuch_config_get_user_primary_email (config);
+   reply = create_reply_message (ctx, config, message, reply_all);
 
-   from_addr = 

[PATCH v5.1 4/4] emacs: Use the new JSON reply format and message-cite-original

2012-02-14 Thread Adam Wolfe Gordon
Using the new JSON reply format allows emacs to quote HTML parts
nicely by using mm-display-part to turn them into displayable text,
then quoting them with message-cite-original. This is very useful for
users who regularly receive HTML-only email.

Use message-mode's message-cite-original function to create the
quoted body for reply messages. In order to make this act like the
existing notmuch defaults, you will need to set the following in
your emacs configuration:

message-citation-line-format On %a, %d %b %Y, %f wrote:
message-citation-line-function 'message-insert-formatted-citation-line

The test has been updated to reflect the (ugly) emacs default.
---

Adjusted the header display to be consistent with the other JSON formats.

 emacs/notmuch-lib.el  |   39 +++
 emacs/notmuch-mua.el  |  123 +++--
 emacs/notmuch-show.el |   24 +-
 test/emacs|  101 +++-
 4 files changed, 228 insertions(+), 59 deletions(-)

diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
index d315f76..3fc7aff 100644
--- a/emacs/notmuch-lib.el
+++ b/emacs/notmuch-lib.el
@@ -21,6 +21,8 @@
 
 ;; This is an part of an emacs-based interface to the notmuch mail system.
 
+(eval-when-compile (require 'cl))
+
 (defvar notmuch-command notmuch
   Command to run the notmuch binary.)
 
@@ -173,6 +175,43 @@ the user hasn't set this variable with the old or new 
value.
   (list 'when ( emacs-major-version 23)
form))
 
+(defun notmuch-split-content-type (content-type)
+  Split content/type into 'content' and 'type'
+  (split-string content-type /))
+
+(defun notmuch-match-content-type (t1 t2)
+  Return t if t1 and t2 are matching content types, taking wildcards into 
account
+  (let ((st1 (notmuch-split-content-type t1))
+   (st2 (notmuch-split-content-type t2)))
+(if (or (string= (cadr st1) *)
+   (string= (cadr st2) *))
+   (string= (car st1) (car st2))
+  (string= t1 t2
+
+(defvar notmuch-multipart/alternative-discouraged
+  '(
+;; Avoid HTML parts.
+text/html
+;; multipart/related usually contain a text/html part and some associated 
graphics.
+multipart/related
+))
+
+(defun notmuch-multipart/alternative-choose (types)
+  Return a list of preferred types from the given list of types
+  ;; Based on `mm-preferred-alternative-precedence'.
+  (let ((seq types))
+(dolist (pref (reverse notmuch-multipart/alternative-discouraged))
+  (dolist (elem (copy-sequence seq))
+   (when (string-match pref elem)
+ (setq seq (nconc (delete elem seq) (list elem))
+seq))
+
+(defun notmuch-parts-filter-by-type (parts type)
+  Given a vector of message parts, return a vector containing the ones 
matching the given type.
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) type)
+   vconcat (list part)))
+
 ;; Compatibility functions for versions of emacs before emacs 23.
 ;;
 ;; Both functions here were copied from emacs 23 with the following copyright:
diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 4be7c13..c2fc8c5 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -19,11 +19,15 @@
 ;;
 ;; Authors: David Edmondson d...@dme.org
 
+(require 'json)
 (require 'message)
+(require 'format-spec)
 
 (require 'notmuch-lib)
 (require 'notmuch-address)
 
+(eval-when-compile (require 'cl))
+
 ;;
 
 (defcustom notmuch-mua-send-hook '(notmuch-mua-message-send-hook)
@@ -72,56 +76,105 @@ list.
(push header message-hidden-headers)))
notmuch-mua-hidden-headers))
 
+(defun notmuch-mua-get-displayed-part (part query-string)
+  (with-temp-buffer
+(if (assq 'content part)
+   (insert (cdr (assq 'content part)))
+  (call-process notmuch-command nil t nil show --format=raw
+   (format --part=%s (cdr (assq 'id part)))
+   query-string))
+
+(let ((handle (mm-make-handle (current-buffer) (list (cdr (assq 
'content-type part)
+ (end-of-orig (point-max)))
+  (mm-display-part handle)
+  (delete-region (point-min) end-of-orig)
+  (buffer-substring (point-min) (point-max)
+
+(defun notmuch-mua-multipart/*-to-list (parts)
+  (loop for part across parts
+   collect (cdr (assq 'content-type part
+
+(defun notmuch-mua-get-quotable-parts (parts)
+  (loop for part across parts
+   if (notmuch-match-content-type (cdr (assq 'content-type part)) 
multipart/alternative)
+ append (let* ((subparts (cdr (assq 'content part)))
+   (types (notmuch-mua-multipart/*-to-list subparts))
+   (chosen-type (car (notmuch-multipart/alternative-choose 
types
+  (notmuch-mua-get-quotable-parts 
(notmuch-parts-filter-by-type subparts chosen-type)))
+   else if (notmuch-match-content-type (cdr (assq 'content-type part)) 
multipart/*)
+ append 

Re: [PATCH v5 0/4] Reply enhancements (was: quoting HTML email ...)

2012-02-14 Thread Adam Wolfe Gordon
I've just sent new versions of parts 1, 2, and 4. The only change is
that I've changed the output of the reply headers to be consistent
with the other JSON formats (capitalized, in the right order). Of
course, that had a ripple effect on the tests and emacs.

Sorry for the double-mail - I just realized I should change these
things while reviewing Austin's patches.
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 0/8] Rewrite JSON show format

2012-02-14 Thread Adam Wolfe Gordon
Hi Austin,

On Tue, Feb 14, 2012 at 10:33, Austin Clements amdra...@mit.edu wrote:
 The saga continues.  As for the text format, this first shifts lots of
 code around without changing its semantics, then it dives in and
 simplifies a lot of things.  Don't be put off by the number of
 patches; most of them are straightforward.

I haven't looked thoroughly style-wise, but otherwise everything LGTM.
I really like the new formatters - their operation is much easier to
follow than the old ones.

 As an added bonus, I documented (!) the JSON format for both show and
 search.

Good idea. I'll add my JSON reply format to this once it seems final.
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch