Re: message-default-mail-headers not working in notmuch 0.22
[notmuch-archives.git] / 1f / 11655ea65f9051d17114dc72d248134791f946
1 Return-Path: <m.walters@qmul.ac.uk>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 21BA8431FC0\r
6         for <notmuch@notmuchmail.org>; Sat, 22 Mar 2014 04:44:35 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -1.098\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5\r
12         tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
13         NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
14 Received: from olra.theworths.org ([127.0.0.1])\r
15         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
16         with ESMTP id MV3vWZ7vlNgX for <notmuch@notmuchmail.org>;\r
17         Sat, 22 Mar 2014 04:44:27 -0700 (PDT)\r
18 Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
19         (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 09C36431FBC\r
22         for <notmuch@notmuchmail.org>; Sat, 22 Mar 2014 04:44:27 -0700 (PDT)\r
23 Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
24         by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
25         (envelope-from <m.walters@qmul.ac.uk>)\r
26         id 1WRKLc-00060L-Kt; Sat, 22 Mar 2014 11:44:21 +0000\r
27 Received: from 93-97-24-31.zone5.bethere.co.uk ([93.97.24.31] helo=localhost)\r
28         by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.71)\r
29         (envelope-from <m.walters@qmul.ac.uk>)\r
30         id 1WRKLb-00046f-T7; Sat, 22 Mar 2014 11:44:20 +0000\r
31 From: Mark Walters <markwalters1009@gmail.com>\r
32 To: Austin Clements <amdragon@MIT.EDU>\r
33 Subject: Re: [Patch v3 5/8] emacs: show: mark tags changed since buffer loaded\r
34 In-Reply-To: <87pplek4v0.fsf@qmul.ac.uk>\r
35 References: <1394597397-8486-1-git-send-email-markwalters1009@gmail.com>\r
36         <1394597397-8486-6-git-send-email-markwalters1009@gmail.com>\r
37         <20140322033933.GC31187@mit.edu> <87pplek4v0.fsf@qmul.ac.uk>\r
38 User-Agent: Notmuch/0.15.2+615~g78e3a93 (http://notmuchmail.org) Emacs/23.4.1\r
39         (x86_64-pc-linux-gnu)\r
40 Date: Sat, 22 Mar 2014 11:44:18 +0000\r
41 Message-ID: <87mwgih2zx.fsf@qmul.ac.uk>\r
42 MIME-Version: 1.0\r
43 Content-Type: text/plain; charset=us-ascii\r
44 X-Sender-Host-Address: 93.97.24.31\r
45 X-QM-Geographic: According to ripencc,\r
46         this message was delivered by a machine in Britain (UK) (GB).\r
47 X-QM-SPAM-Info: Sender has good ham record.  :)\r
48 X-QM-Body-MD5: fea0858007c2b9064a64c69cb1ffd51b (of first 20000 bytes)\r
49 X-SpamAssassin-Score: 0.0\r
50 X-SpamAssassin-SpamBar: /\r
51 X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
52         determine if it is\r
53         spam. We require at least 5.0 points to mark a message as spam.\r
54         This message scored 0.0 points. Summary of the scoring: \r
55         * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
56         provider *      (markwalters1009[at]gmail.com)\r
57         * -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay\r
58         *      domain\r
59         *  0.0 AWL AWL: From: address is in the auto white-list\r
60 X-QM-Scan-Virus: ClamAV says the message is clean\r
61 Cc: notmuch@notmuchmail.org\r
62 X-BeenThere: notmuch@notmuchmail.org\r
63 X-Mailman-Version: 2.1.13\r
64 Precedence: list\r
65 List-Id: "Use and development of the notmuch mail system."\r
66         <notmuch.notmuchmail.org>\r
67 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
68         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
69 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
70 List-Post: <mailto:notmuch@notmuchmail.org>\r
71 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
72 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
73         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
74 X-List-Received-Date: Sat, 22 Mar 2014 11:44:35 -0000\r
75 \r
76 \r
77 On Sat, 22 Mar 2014, Mark Walters <markwalters1009@gmail.com> wrote:\r
78 > Hi\r
79 >\r
80 > Ok I have something working based on your version below. I will post\r
81 > once I have tested a little more. Since the diff with my version will be\r
82 > quite big I will just comment on the differences from your version.\r
83 >\r
84 > On Sat, 22 Mar 2014, Austin Clements <amdragon@MIT.EDU> wrote:\r
85 >> Quoth Mark Walters on Mar 12 at  4:09 am:\r
86 >>> This allows (and requires) the original-tags to be passed along with\r
87 >>> the current-tags to be passed to notmuch-tag-format-tags. This allows\r
88 >>> the tag formatting to show added and deleted tags.By default a removed\r
89 >>> tag is displayed with strike-through in red (if strike-through is not\r
90 >>> available, eg on a terminal, inverse video is used instead) and an\r
91 >>> added tag is displayed underlined in green.\r
92 >>> \r
93 >>> If the caller does not wish to use the new feature it can pass\r
94 >>> current-tags for both arguments and, at this point, we do exactly that\r
95 >>> in the three callers of this function.\r
96 >>> \r
97 >>> Note, we cannot tidily allow original-tags to be optional because we would\r
98 >>> need to distinguish nil meaning "we are not specifying original-tags"\r
99 >>> from nil meaning there were no original-tags (an empty list).\r
100 >>> \r
101 >>> We use this in subsequent patches to make it clear when a message was\r
102 >>> unread when you first loaded a show buffer (previously the unread tag\r
103 >>> could be removed before a user realised that it had been unread).\r
104 >>> \r
105 >>> The code adds into the existing tag formatting code. The user can\r
106 >>> specify exactly how a tag should be displayed normally, when deleted,\r
107 >>> or when added.\r
108 >>> \r
109 >>> Since the formatting code matches regexps a user can match all deleted\r
110 >>> tags with a ".*" in notmuch-tag-deleted-formats.  For example setting\r
111 >>> notmuch-tag-deleted-formats to '((".*" nil)) tells notmuch not to show\r
112 >>> deleted tags at all.\r
113 >>> \r
114 >>> All the variables are customizable; however, more complicated cases\r
115 >>> like changing the face depending on the type of display will require\r
116 >>> custom lisp.\r
117 >>> \r
118 >>> Currently this overrides notmuch-tag-deleted-formats for the tests\r
119 >>> setting it to '((".*" nil)) so that they get removed from the display\r
120 >>> and, thus, all tests still pass.\r
121 >>> ---\r
122 >>>  emacs/notmuch-show.el |    4 +-\r
123 >>>  emacs/notmuch-tag.el  |   72 +++++++++++++++++++++++++++++++++++-------------\r
124 >>>  emacs/notmuch-tree.el |    2 +-\r
125 >>>  emacs/notmuch.el      |    2 +-\r
126 >>>  test/test-lib.el      |    5 +++\r
127 >>>  5 files changed, 61 insertions(+), 24 deletions(-)\r
128 >>> \r
129 >>> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
130 >>> index 019f51d..5492be4 100644\r
131 >>> --- a/emacs/notmuch-show.el\r
132 >>> +++ b/emacs/notmuch-show.el\r
133 >>> @@ -344,7 +344,7 @@ (defun notmuch-show-update-tags (tags)\r
134 >>>      (if (re-search-forward "(\\([^()]*\\))$" (line-end-position) t)\r
135 >>>     (let ((inhibit-read-only t))\r
136 >>>       (replace-match (concat "("\r
137 >>> -                            (notmuch-tag-format-tags tags)\r
138 >>> +                            (notmuch-tag-format-tags tags tags)\r
139 >>>                              ")"))))))\r
140 >>>  \r
141 >>>  (defun notmuch-clean-address (address)\r
142 >>> @@ -423,7 +423,7 @@ (defun notmuch-show-insert-headerline (headers date tags depth)\r
143 >>>         " ("\r
144 >>>         date\r
145 >>>         ") ("\r
146 >>> -       (notmuch-tag-format-tags tags)\r
147 >>> +       (notmuch-tag-format-tags tags tags)\r
148 >>>         ")\n")\r
149 >>>      (overlay-put (make-overlay start (point)) 'face 'notmuch-message-summary-face)))\r
150 >>>  \r
151 >>> diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el\r
152 >>> index 4698856..cfccb8e 100644\r
153 >>> --- a/emacs/notmuch-tag.el\r
154 >>> +++ b/emacs/notmuch-tag.el\r
155 >>> @@ -184,45 +184,77 @@ (defun notmuch-tag-clear-cache ()\r
156 >>>    "Clear the internal cache of tag formats."\r
157 >>>    (clrhash notmuch-tag--format-cache))\r
158 >>>  \r
159 >>> -(defun notmuch-tag-format-tag (tag)\r
160 >>> -  "Format TAG by according to `notmuch-tag-formats'.\r
161 >>> -\r
162 >>> -Callers must ensure that the tag format cache has been recently cleared\r
163 >>> -via `notmuch-tag-clear-cache' before using this function.  For example,\r
164 >>> -it would be appropriate to clear the cache just prior to filling a\r
165 >>> -buffer that uses formatted tags."\r
166 >>> -\r
167 >>> -  (let ((formatted (gethash tag notmuch-tag--format-cache 'missing)))\r
168 >>> +(defun notmuch-tag-format-tag-by-state (tag formatted-tag tag-state)\r
169 >>> +  "Format TAG according to the appropriate `notmuch-tag-formats`.\r
170 >>> +\r
171 >>> +Applies formats for TAG from the appropriate one of\r
172 >>> +`notmuch-tag-formats`, `notmuch-tag-deleted-formats` and\r
173 >>> +`notmuch-tag-added-formats` based on TAG-STATE to the partially\r
174 >>\r
175 >> The second ` should be a ' on all four of the above references.\r
176 >>\r
177 >>> +formatted tag FORMATTED-TAG."\r
178 >>> +  (let ((formatted (gethash (cons tag tag-state) notmuch-tag--format-cache 'missing)))\r
179 >>\r
180 >> Something's strange here.  If this hits in the cache, it will ignore\r
181 >> formatted-tag.  I can't actually construct a situation where this does\r
182 >> the wrong thing, but it always seems to do the right thing for the\r
183 >> wrong reasons.\r
184 >>\r
185 >> This code would make a lot more sense to me if it were turned\r
186 >> inside-out with `notmuch-tag-format-tag':\r
187 >>\r
188 >> (defun notmuch-tag-format-tag (tags orig-tags tag)\r
189 >>   "Format TAG according to `notmuch-tag-formats'.\r
190 >>\r
191 >> TAGS and ORIG-TAGS are lists of the current tags and the original\r
192 >> tags; tags which have been deleted (i.e., are in ORIG-TAGS but\r
193 >> are not in TAGS) are shown using formats from\r
194 >> `notmuch-tag-deleted-formats'; tags which have been added (i.e.,\r
195 >> are in TAGS but are not in ORIG-TAGS) are shown using formats\r
196 >> from `notmuch-tag-added-formats' and tags which have not been\r
197 >> changed (the normal case) are shown using formats from\r
198 >> `notmuch-tag-formats'"\r
199 >>   (let* ((tag-state (cond ((not (member tag tags)) 'deleted)\r
200 >>                           ((not (member tag orig-tags)) 'added)))\r
201 >>          (formatted (gethash (cons tag tag-state) notmuch-tag--format-cache\r
202 >>                              'missing)))\r
203 >>     (when (eq formatted 'missing)\r
204 >\r
205 > I changed formatted to formatted-tag.\r
206 >\r
207 >>       (let ((base (notmuch-tag--get-formats tag notmuch-tag-formats))\r
208 >>             (over (case tag-state\r
209 >>                         ((nil) nil)\r
210 >>                         (deleted (notmuch-tag--get-formats \r
211 >>                                   tag notmuch-tag-deleted-formats))\r
212 >>                         (added (notmuch-tag--get-formats \r
213 >>                                 tag notmuch-tag-deleted-formats)))))\r
214 >\r
215 > I moved the nil case to an otherwise case (and corrected the typo of\r
216 > deleted-formats to added-formats in the second clause).\r
217 >\r
218 >>         (setq formatted (notmuch-tag--do-format\r
219 >>                          (notmuch-tag--do-format tag) base over))\r
220 >\r
221 > I split this into two steps. I also slightly changed\r
222 > notmuch-tag--do-format so it gets passed tag and formatted-tag\r
223 >\r
224 >>         (puthash (cons tag tag-state) formatted notmuch-tag--format-cache)))\r
225 >>     formatted))\r
226 >>\r
227 >> (defun notmuch-tag--get-formats (tag format-alist)\r
228 >>   "Find the first item whose car regexp-matches TAG."\r
229 >>   (save-match-data\r
230 >>     ;; Don't use assoc-default since there's no way to distinguish a\r
231 >>     ;; missing key from a present key with a null cdr.\r
232 >>     (assoc* tag format-alist\r
233 >>             :test (lambda (tag key)\r
234 >>                     (and (eq (string-match key tag) 0)\r
235 >>                           (= (match-end 0) (length tag)))))))\r
236 >\r
237 > I have not changed this.\r
238 >\r
239 >> (defun notmuch-tag--do-format (tag formats)\r
240 >>   "Apply a tag-formats entry to TAG."\r
241 >\r
242 > We need to pass formatted-tag as well as the original unformatted tag\r
243 > because we want to do the look up to decide what to do based on the\r
244 > original tag not whatever it has become.\r
245 \r
246 Ok I have realised I was wrong and your version was fine (as the choice\r
247 of formats was already made). However, I do still pass the unformatted\r
248 tag so that the formatter can access that as `bare-tag'.\r
249 \r
250 >>   (cond ((null formats)              ;; - Tag not in `formats',\r
251 >>          tag)                        ;;   the format is the tag itself.\r
252 >>         ((null (cdr formats))        ;; - Tag was deliberately hidden,\r
253 >>          nil)                        ;;   no format must be returned\r
254 >>         (t\r
255 >>          ;; Tag was found and has formats, we must apply all the\r
256 >>          ;; formats.  TAG may be null so treat that as a special case.\r
257 >>          (let ((old-tag tag) (tag (or tag "")))\r
258 >>            (dolist (format (cdr formats))\r
259 >>              (setq tag (eval format)))\r
260 >>            (if (and (null old-tag) (equal tag ""))\r
261 >>                nil\r
262 >>              tag)))))\r
263 \r
264 More importantly I was getting (erratic) weird effects because some of\r
265 these changes changed tag (and even the copy of tag outside this\r
266 function). I fixed this by using copy-sequence for the second let\r
267 clause.\r
268 \r
269 Best wishes \r
270 \r
271 Mark\r
272 \r
273 \r
274 > This got slightly tweaked because of the formatted/original split above.\r
275 >\r
276 >\r
277 >> (Completely untested and all indented with spaces and probably\r
278 >> incorrectly because I wrote it all in my email buffer, but you get the\r
279 >> idea.)\r
280 >\r
281 > And I think I fixed all the whitespace\r
282 >\r
283 > This looks much nicer than my version. Thanks!\r
284 >\r
285 > Best wishes\r
286 >\r
287 > Mark\r
288 >\r
289 >\r
290 >\r
291 >>\r
292 >>>      (when (eq formatted 'missing)\r
293 >>> -      (let* ((formats\r
294 >>> +      (let* ((tag-formats (case tag-state\r
295 >>> +                           ((list nil) notmuch-tag-formats)\r
296 >>\r
297 >> While this isn't *technically* wrong, I don't think you meant to\r
298 >> accept a tag-state of 'list.  Should be\r
299 >>\r
300 >> (case tag-state\r
301 >>    ((nil) notmuch-tag-formats)\r
302 >>    (deleted ...\r
303 >>\r
304 >>> +                           (deleted notmuch-tag-deleted-formats)\r
305 >>> +                           (added notmuch-tag-added-formats)))\r
306 >>> +        (formats\r
307 >>>           (save-match-data\r
308 >>>             ;; Don't use assoc-default since there's no way to\r
309 >>>             ;; distinguish a missing key from a present key with a\r
310 >>>             ;; null cdr:.\r
311 >>> -           (assoc* tag notmuch-tag-formats\r
312 >>> +           (assoc* tag tag-formats\r
313 >>>                     :test (lambda (tag key)\r
314 >>>                             (and (eq (string-match key tag) 0)\r
315 >>>                                  (= (match-end 0) (length tag))))))))\r
316 >>>     (setq formatted\r
317 >>>           (cond\r
318 >>> -          ((null formats)          ;; - Tag not in `notmuch-tag-formats',\r
319 >>> -           tag)                    ;;   the format is the tag itself.\r
320 >>> +          ((null formats)          ;; - Tag not in `tag-formats',\r
321 >>> +           formatted-tag)          ;;   the format is the tag itself.\r
322 >>>            ((null (cdr formats))    ;; - Tag was deliberately hidden,\r
323 >>>             nil)                    ;;   no format must be returned\r
324 >>> -          (t                       ;; - Tag was found and has formats,\r
325 >>> -           (let ((tag tag))        ;;   we must apply all the formats.\r
326 >>> +          (t\r
327 >>> +           ;; Tag was found and has formats, we must apply all\r
328 >>> +           ;; the formats.  FORMATTED-TAG may be null so treat\r
329 >>> +           ;; that as a special case.\r
330 >>> +           (let ((tag (or formatted-tag "")))\r
331 >>>               (dolist (format (cdr formats) tag)\r
332 >>> -               (setq tag (eval format)))))))\r
333 >>> -   (puthash tag formatted notmuch-tag--format-cache)))\r
334 >>> +               (setq tag (eval format)))\r
335 >>> +             (if (and (null formatted-tag)\r
336 >>> +                      (equal tag ""))\r
337 >>> +                 nil\r
338 >>> +               tag)))))\r
339 >>> +   (puthash (cons tag tag-state) formatted notmuch-tag--format-cache)))\r
340 >>>      formatted))\r
341 >>>  \r
342 >>> -(defun notmuch-tag-format-tags (tags &optional face)\r
343 >>> +(defun notmuch-tag-format-tag (tags orig-tags tag)\r
344 >>> +  "Format TAG according to `notmuch-tag-formats'.\r
345 >>> +\r
346 >>> +TAGS and ORIG-TAGS are lists of the current tags and the original\r
347 >>> +tags; tags which have been deleted (i.e., are in ORIG-TAGS but\r
348 >>> +are not in TAGS) are shown using formats from\r
349 >>> +`notmuch-tag-deleted-formats'; tags which have been added (i.e.,\r
350 >>> +are in TAGS but are not in ORIG-TAGS) are shown using formats\r
351 >>> +from `notmuch-tag-added-formats' and tags which have not been\r
352 >>> +changed (the normal case) are shown using formats from\r
353 >>> +`notmuch-tag-formats'"\r
354 >>> +  (let* ((formatted-tag (notmuch-tag-format-tag-by-state tag tag nil)))\r
355 >>> +    (cond ((not (member tag tags))\r
356 >>> +      (notmuch-tag-format-tag-by-state tag formatted-tag 'deleted))\r
357 >>> +     ((not (member tag orig-tags))\r
358 >>> +      (notmuch-tag-format-tag-by-state tag formatted-tag 'added))\r
359 >>> +     (t\r
360 >>> +       formatted-tag))))\r
361 >>> +\r
362 >>> +(defun notmuch-tag-format-tags (tags orig-tags &optional face)\r
363 >>>    "Return a string representing formatted TAGS."\r
364 >>> -  (let ((face (or face 'notmuch-tag-face)))\r
365 >>> +  (let ((face (or face 'notmuch-tag-face))\r
366 >>> +   (all-tags (sort (delete-dups (append tags orig-tags nil)) #'string<)))\r
367 >>>      (notmuch-apply-face\r
368 >>>       (mapconcat #'identity\r
369 >>>             ;; nil indicated that the tag was deliberately hidden\r
370 >>> -           (delq nil (mapcar #'notmuch-tag-format-tag tags))\r
371 >>> +           (delq nil (mapcar\r
372 >>> +                      (apply-partially #'notmuch-tag-format-tag tags orig-tags)\r
373 >>> +                      all-tags))\r
374 >>>             " ")\r
375 >>>       face\r
376 >>>       t)))\r
377 >>> diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el\r
378 >>> index c78d9de..8bf2fbc 100644\r
379 >>> --- a/emacs/notmuch-tree.el\r
380 >>> +++ b/emacs/notmuch-tree.el\r
381 >>> @@ -704,7 +704,7 @@ (defun notmuch-tree-format-field (field format-string msg)\r
382 >>>         (face (if match\r
383 >>>                   'notmuch-tree-match-tag-face\r
384 >>>                 'notmuch-tree-no-match-tag-face)))\r
385 >>> -   (format format-string (notmuch-tag-format-tags tags face)))))))\r
386 >>> +   (format format-string (notmuch-tag-format-tags tags tags face)))))))\r
387 >>>  \r
388 >>>  (defun notmuch-tree-format-field-list (field-list msg)\r
389 >>>    "Format fields of MSG according to FIELD-LIST and return string"\r
390 >>> diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
391 >>> index 93a6d8b..609f408 100644\r
392 >>> --- a/emacs/notmuch.el\r
393 >>> +++ b/emacs/notmuch.el\r
394 >>> @@ -754,7 +754,7 @@ (defun notmuch-search-insert-field (field format-string result)\r
395 >>>  \r
396 >>>     ((string-equal field "tags")\r
397 >>>      (let ((tags (plist-get result :tags)))\r
398 >>> -      (insert (format format-string (notmuch-tag-format-tags tags)))))))\r
399 >>> +      (insert (format format-string (notmuch-tag-format-tags tags tags)))))))\r
400 >>>  \r
401 >>>  (defun notmuch-search-show-result (result &optional pos)\r
402 >>>    "Insert RESULT at POS or the end of the buffer if POS is null."\r
403 >>> diff --git a/test/test-lib.el b/test/test-lib.el\r
404 >>> index 37fcb3d..437f83f 100644\r
405 >>> --- a/test/test-lib.el\r
406 >>> +++ b/test/test-lib.el\r
407 >>> @@ -165,3 +165,8 @@ (defun notmuch-test-expect-equal (output expected)\r
408 >>>  \r
409 >>>       (t\r
410 >>>        (notmuch-test-report-unexpected output expected)))))\r
411 >>> +\r
412 >>> +;; For historical reasons, we hide deleted tags by default in the test\r
413 >>> +;; suite\r
414 >>> +(setq notmuch-tag-deleted-formats\r
415 >>> +      '((".*" nil)))\r