Re: notmuch.el: controlling what does and doesn't get expanded in searches
[notmuch-archives.git] / db / 0616f5f11ce82ae576c3dfcb05b2b83603031a
1 Return-Path: <amdragon@mit.edu>\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 2D568431FB6\r
6         for <notmuch@notmuchmail.org>; Mon, 25 Mar 2013 07:22:43 -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: -0.7\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id C5fm8VpjgJun for <notmuch@notmuchmail.org>;\r
16         Mon, 25 Mar 2013 07:22:42 -0700 (PDT)\r
17 Received: from dmz-mailsec-scanner-7.mit.edu (DMZ-MAILSEC-SCANNER-7.MIT.EDU\r
18         [18.7.68.36])\r
19         by olra.theworths.org (Postfix) with ESMTP id C2E21431FAF\r
20         for <notmuch@notmuchmail.org>; Mon, 25 Mar 2013 07:22:41 -0700 (PDT)\r
21 X-AuditID: 12074424-b7f936d0000008eb-11-51505db11670\r
22 Received: from mailhub-auth-2.mit.edu ( [18.7.62.36])\r
23         by dmz-mailsec-scanner-7.mit.edu (Symantec Messaging Gateway) with SMTP\r
24         id 32.13.02283.1BD50515; Mon, 25 Mar 2013 10:22:41 -0400 (EDT)\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH-1.MIT.EDU [18.9.28.11])\r
26         by mailhub-auth-2.mit.edu (8.13.8/8.9.2) with ESMTP id r2PEMd3d002348; \r
27         Mon, 25 Mar 2013 10:22:40 -0400\r
28 Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
29         (authenticated bits=0)\r
30         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
31         by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id r2PEMbLa025826\r
32         (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NOT);\r
33         Mon, 25 Mar 2013 10:22:38 -0400\r
34 Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.80)\r
35         (envelope-from <amdragon@mit.edu>)\r
36         id 1UK8IH-0005in-BZ; Mon, 25 Mar 2013 10:22:37 -0400\r
37 Date: Mon, 25 Mar 2013 10:22:37 -0400\r
38 From: Austin Clements <amdragon@MIT.EDU>\r
39 To: Damien Cassou <damien.cassou@gmail.com>\r
40 Subject: Re: [PATCH 2/2] emacs: possibility to customize the rendering of tags\r
41 Message-ID: <20130325142237.GB32584@mit.edu>\r
42 References: <1364038194-19856-1-git-send-email-damien.cassou@gmail.com>\r
43         <1364038194-19856-3-git-send-email-damien.cassou@gmail.com>\r
44 MIME-Version: 1.0\r
45 Content-Type: text/plain; charset=iso-8859-1\r
46 Content-Disposition: inline\r
47 Content-Transfer-Encoding: 8bit\r
48 In-Reply-To: <1364038194-19856-3-git-send-email-damien.cassou@gmail.com>\r
49 User-Agent: Mutt/1.5.21 (2010-09-15)\r
50 X-Brightmail-Tracker:\r
51  H4sIAAAAAAAAA+NgFlrOKsWRmVeSWpSXmKPExsUixG6norsxNiDQ4ON2Lotdd7cyWVy/OZPZ\r
52         gclj56y77B7PVt1iDmCK4rJJSc3JLEst0rdL4MrYdKyk4E5IxaPmMywNjG+cuxg5OSQETCTu\r
53         XN/OBmGLSVy4tx7I5uIQEtjHKHHzTwMzhLORUeLdrHksEM5pJol58yYzQThLGCUu7V0C1s8i\r
54         oCox68UDRhCbTUBDYtv+5WC2iIC2xJulHawgNrOAtMS3381MILawgJ/Ewx8nwWxeAR2J49ef\r
55         MoPYQgKtjBI791lBxAUlTs58wgLRqyOxc+sdoF0cYHOW/+OACMtLNG+dDdbKKeAhcefCPbBV\r
56         ogIqElNObmObwCg8C8mkWUgmzUKYNAvJpAWMLKsYZVNyq3RzEzNzilOTdYuTE/PyUot0zfVy\r
57         M0v0UlNKNzGC48BFZQdj8yGlQ4wCHIxKPLwbggMChVgTy4orcw8xSnIwKYnyloYDhfiS8lMq\r
58         MxKLM+KLSnNSiw8xSnAwK4nwagkB5XhTEiurUovyYVLSHCxK4rzXU276CwmkJ5akZqemFqQW\r
59         wWRlODiUJHg/xgA1ChalpqdWpGXmlCCkmTg4QYbzAA1/DVLDW1yQmFucmQ6RP8Woy/F13udX\r
60         jEIsefl5qVLivOdBigRAijJK8+DmwNLXK0ZxoLeEef+AVPEAUx/cpFdAS5iAlkz96w+ypCQR\r
61         ISXVwGiwlkMr6NXa1p/bw2fLlFtGPd17aXKEZ07X9mOmj6K+c/zgttrHsNig4XO8BIPuqfMC\r
62         5myq54MXbYpzPzr1hafZpm7f+AWTtXbMmT0tKc/kvn7wNiaDzQFnyh+vadPQLHrw+kb3eYvp\r
63         E1fdemgQEtd/JfXaHyf2Tb/npi0N8+2Q7IwM/lT5fp4SS3FGoqEWc1FxIgAgBc4/OgMAAA==\r
64 Cc: notmuch@notmuchmail.org\r
65 X-BeenThere: notmuch@notmuchmail.org\r
66 X-Mailman-Version: 2.1.13\r
67 Precedence: list\r
68 List-Id: "Use and development of the notmuch mail system."\r
69         <notmuch.notmuchmail.org>\r
70 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
71         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
72 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
73 List-Post: <mailto:notmuch@notmuchmail.org>\r
74 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
75 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
76         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
77 X-List-Received-Date: Mon, 25 Mar 2013 14:22:43 -0000\r
78 \r
79 Series LGTM.  I noticed a few things below that would be fine to tweak\r
80 in a follow-up trivial patch or two.\r
81 \r
82 Quoth Damien Cassou on Mar 23 at 12:29 pm:\r
83 > This patch extracts the rendering of tags in notmuch-show to\r
84 > the notmuch-tag file.\r
85\r
86 > This file introduces a `notmuch-tag-formats' variable that associates\r
87 > each tag to a particular format. This variable can be customized\r
88 > thanks to the work of Austin Clements. For example,\r
89\r
90 >   '(("unread" (propertize tag 'face '(:foreground "red")))\r
91 >     ("flagged" (notmuch-tag-format-image tag "star.svg")))\r
92\r
93 > associates a red foreground to the "unread" tag and a star picture to\r
94 > the "flagged" tag.\r
95\r
96 > Signed-off-by: Damien Cassou <damien.cassou@gmail.com>\r
97 > ---\r
98 >  emacs/notmuch-show.el |    6 +--\r
99 >  emacs/notmuch-tag.el  |  136 ++++++++++++++++++++++++++++++++++++++++++++++++-\r
100 >  emacs/notmuch.el      |    5 +-\r
101 >  3 files changed, 139 insertions(+), 8 deletions(-)\r
102\r
103 > diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
104 > index acaef8e..a4d2c12 100644\r
105 > --- a/emacs/notmuch-show.el\r
106 > +++ b/emacs/notmuch-show.el\r
107 > @@ -362,8 +362,7 @@ operation on the contents of the current buffer."\r
108 >      (if (re-search-forward "(\\([^()]*\\))$" (line-end-position) t)\r
109 >       (let ((inhibit-read-only t))\r
110 >         (replace-match (concat "("\r
111 > -                              (propertize (mapconcat 'identity tags " ")\r
112 > -                                          'face 'notmuch-tag-face)\r
113 > +                              (notmuch-tag-format-tags tags)\r
114 >                                ")"))))))\r
115 >  \r
116 >  (defun notmuch-clean-address (address)\r
117 > @@ -441,8 +440,7 @@ message at DEPTH in the current thread."\r
118 >           " ("\r
119 >           date\r
120 >           ") ("\r
121 > -         (propertize (mapconcat 'identity tags " ")\r
122 > -                     'face 'notmuch-tag-face)\r
123 > +         (notmuch-tag-format-tags tags)\r
124 >           ")\n")\r
125 >      (overlay-put (make-overlay start (point)) 'face 'notmuch-message-summary-face)))\r
126 >  \r
127 > diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el\r
128 > index 4fce3a9..75a438b 100644\r
129 > --- a/emacs/notmuch-tag.el\r
130 > +++ b/emacs/notmuch-tag.el\r
131 > @@ -1,5 +1,6 @@\r
132 >  ;; notmuch-tag.el --- tag messages within emacs\r
133 >  ;;\r
134 > +;; Copyright © Damien Cassou\r
135 >  ;; Copyright © Carl Worth\r
136 >  ;;\r
137 >  ;; This file is part of Notmuch.\r
138 > @@ -18,11 +19,144 @@\r
139 >  ;; along with Notmuch.  If not, see <http://www.gnu.org/licenses/>.\r
140 >  ;;\r
141 >  ;; Authors: Carl Worth <cworth@cworth.org>\r
142 > +;;          Damien Cassou <damien.cassou@gmail.com>\r
143 > +;;\r
144 > +;;; Code:\r
145 > +;;\r
146 >  \r
147 > -(eval-when-compile (require 'cl))\r
148 > +(require 'cl)\r
149 >  (require 'crm)\r
150 >  (require 'notmuch-lib)\r
151 >  \r
152 > +(defcustom notmuch-tag-formats\r
153 > +  '(("unread" (propertize tag 'face '(:foreground "red")))\r
154 > +    ("flagged" (notmuch-tag-format-image-data tag (notmuch-tag-star-icon))))\r
155 > +  "Custom formats for individual tags.\r
156 > +\r
157 > +This gives a list that maps from tag names to lists of formatting\r
158 > +expressions.  The car of each element gives a tag name and the\r
159 > +cdr gives a list of Elisp expressions that modify the tag.  If\r
160 > +the list is empty, the tag will simply be hidden.  Otherwise,\r
161 > +each expression will be evaluated in order: for the first\r
162 > +expression, the variable `tag' will be bound to the tag name; for\r
163 > +each later expression, the variable `tag' will be bound to the\r
164 > +result of the previous expression.  In this way, each expression\r
165 > +can build on the formatting performed by the previous expression.\r
166 > +The result of the last expression will displayed in place of the\r
167 > +tag.\r
168 > +\r
169 > +For example, to replace a tag with another string, simply use\r
170 > +that string as a formatting expression.  To change the foreground\r
171 > +of a tag to red, use the expression\r
172 > +  (propertize tag 'face '(:foreground \"red\"))\r
173 > +\r
174 > +See also `notmuch-tag-format-image', which can help replace tags\r
175 > +with images."\r
176 > +\r
177 > +  :group 'notmuch-search\r
178 > +  :group 'notmuch-show\r
179 > +  :type '(alist :key-type (string :tag "Tag")\r
180 > +             :extra-offset -3\r
181 > +             :value-type\r
182 > +             (radio :format "%v"\r
183 > +                    (const :tag "Hidden" nil)\r
184 > +                    (set :tag "Modified"\r
185 > +                         (string :tag "Display as")\r
186 > +                         (list :tag "Face" :extra-offset -4\r
187 > +                               (const :format "" :inline t\r
188 > +                                      (propertize tag 'face))\r
189 > +                               (list :format "%v"\r
190 > +                                     (const :format "" quote)\r
191 > +                                     custom-face-edit))\r
192 > +                         (list :format "%v" :extra-offset -4\r
193 > +                               (const :format "" :inline t\r
194 > +                                      (notmuch-tag-format-image-data tag))\r
195 > +                               (choice :tag "Image"\r
196 > +                                       (const :tag "Star"\r
197 > +                                              (notmuch-tag-star-icon))\r
198 > +                                       (const :tag "Empty star"\r
199 > +                                              (notmuch-tag-star-empty-icon))\r
200 > +                                       (const :tag "Tag"\r
201 > +                                              (notmuch-tag-tag-icon))\r
202 > +                                       (string :tag "Custom")))\r
203 > +                         (sexp :tag "Custom")))))\r
204 > +\r
205 > +(defun notmuch-tag-format-image-data (tag data)\r
206 > +  "Replace TAG with image DATA, if available.\r
207 > +\r
208 > +This function returns a propertized string that will display image\r
209 > +DATA in place of TAG.This is designed for use in\r
210 \r
211 Missing spaces after the period.\r
212 \r
213 > +`notmuch-tag-formats'.\r
214 > +\r
215 > +DATA is the content of an SVG picture (e.g., as returned by\r
216 > +`notmuch-tag-star-icon')."\r
217 > +  (propertize tag 'display\r
218 > +           `(image :type svg\r
219 > +                   :data ,data\r
220 > +                   :ascent center\r
221 > +                   :mask heuristic)))\r
222 > +\r
223 > +(defun notmuch-tag-star-icon ()\r
224 \r
225 Should these be notmuch-tag-icon-{star,star-empty,tag}?  That would\r
226 better match standard naming conventions (most general term to least\r
227 general term) and would avoid the awkward notmuch-tag-tag-icon.\r
228 \r
229 > +  "Return SVG data representing a star icon.\r
230 > +This can be used with `notmuch-tag-format-image-data'."\r
231 > +"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r
232 > +<svg version=\"1.1\" width=\"16\" height=\"16\">\r
233 > +  <g transform=\"translate(-242.81601,-315.59635)\">\r
234 > +    <path\r
235 > +       d=\"m 290.25762,334.31206 -17.64143,-11.77975 -19.70508,7.85447 5.75171,-20.41814 -13.55925,-16.31348 21.19618,-0.83936 11.325,-17.93675 7.34825,19.89939 20.55849,5.22795 -16.65471,13.13786 z\"\r
236 > +       transform=\"matrix(0.2484147,-0.02623394,0.02623394,0.2484147,174.63605,255.37691)\"\r
237 > +       style=\"fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\" />\r
238 \r
239 Here's an even simpler equivalent SVG:\r
240 \r
241 <?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
242 <svg version="1.1" width="16" height="16">\r
243   <path\r
244       d="M 12.69462,15.21399 8.00320,12.75053 3.31422,15.21864 4.20739,9.99558 0.41110,6.29879 5.65452,5.53422 7.99727,0.78137 10.34472,5.53189 15.58890,6.29126 11.79629,9.99182 z"\r
245       style="fill:#ffff00;stroke:#000000;stroke-width:0.25" />\r
246 </svg>\r
247 \r
248 I pre-applied the transformations and removed the style attributes\r
249 that had default or unimportant values.  (The script to do the path\r
250 math is attached.)\r
251 \r
252 > +  </g>\r
253 > +</svg>")\r
254 > +\r
255 > +(defun notmuch-tag-star-empty-icon ()\r
256 > +  "Return SVG data representing an empty star icon.\r
257 > +This can be used with `notmuch-tag-format-image-data'."\r
258 > +  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r
259 > +<svg version=\"1.1\" width=\"16\" height=\"16\">\r
260 > +  <g transform=\"translate(-242.81601,-315.59635)\">\r
261 > +    <path\r
262 > +       d=\"m 290.25762,334.31206 -17.64143,-11.77975 -19.70508,7.85447 5.75171,-20.41814 -13.55925,-16.31348 21.19618,-0.83936 11.325,-17.93675 7.34825,19.89939 20.55849,5.22795 -16.65471,13.13786 z\"\r
263 > +       transform=\"matrix(0.2484147,-0.02623394,0.02623394,0.2484147,174.63605,255.37691)\"\r
264 > +       style=\"fill:#d6d6d1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\" />\r
265 > +  </g>\r
266 > +</svg>")\r
267 \r
268 <?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
269 <svg version="1.1" width="16" height="16">\r
270   <path\r
271       d="M 12.69462,15.21399 8.00320,12.75053 3.31422,15.21864 4.20739,9.99558 0.41110,6.29879 5.65452,5.53422 7.99727,0.78137 10.34472,5.53189 15.58890,6.29126 11.79629,9.99182 z"\r
272       style="fill:#d6d6d1;stroke:#000000;stroke-width:0.25" />\r
273 </svg>\r
274 \r
275 > +\r
276 > +(defun notmuch-tag-tag-icon ()\r
277 > +  "Return SVG data representing a tag icon.\r
278 > +This can be used with `notmuch-tag-format-image-data'."\r
279 > +  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r
280 > +<svg version=\"1.1\" width=\"16\" height=\"16\">\r
281 > +  <g transform=\"translate(0,-1036.3622)\">\r
282 > +    <path\r
283 > +       d=\"m 0.44642857,1040.9336 12.50000043,0 2.700893,3.6161 -2.700893,3.616 -12.50000043,0 z\"\r
284 > +       style=\"fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1\" />\r
285 > +  </g>\r
286 > +</svg>")\r
287 \r
288 <?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
289 <svg version="1.1" width="16" height="16">\r
290   <path\r
291       d="M 0.44643,4.57140 12.94643,4.57140 15.64732,8.18750 12.94643,11.80350 0.44643,11.80350 z"\r
292       style="fill:#ffff00;stroke:#000000;stroke-width:0.25" />\r
293 </svg>\r
294 \r
295 > +\r
296 > +(defun notmuch-tag-format-tag (tag)\r
297 > +  "Format TAG by looking into `notmuch-tag-formats'."\r
298 > +  (let ((formats (assoc tag notmuch-tag-formats)))\r
299 > +    (cond\r
300 > +     ((null formats)         ;; - Tag not in `notmuch-tag-formats',\r
301 > +      tag)                   ;;   the format is the tag itself.\r
302 > +     ((null (cdr formats))   ;; - Tag was deliberately hidden,\r
303 > +      nil)                   ;;   no format must be returned\r
304 > +     (t                              ;; - Tag was found and has formats,\r
305 > +      (let ((tag tag))               ;;   we must apply all the formats.\r
306 > +     (dolist (format (cdr formats) tag)\r
307 > +       (setq tag (eval format))))))))\r
308 > +\r
309 > +(defun notmuch-tag-format-tags (tags)\r
310 > +  "Return a string representing formatted TAGS."\r
311 > +  (notmuch-combine-face-text-property-string\r
312 > +   (mapconcat #'identity\r
313 > +           ;; nil indicated that the tag was deliberately hidden\r
314 > +           (delq nil (mapcar #'notmuch-tag-format-tag tags))\r
315 > +           " ")\r
316 > +   'notmuch-tag-face\r
317 > +   t))\r
318 > +\r
319 >  (defcustom notmuch-before-tag-hook nil\r
320 >    "Hooks that are run before tags of a message are modified.\r
321 >  \r
322 > diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
323 > index c98a4fe..e58c51d 100644\r
324 > --- a/emacs/notmuch.el\r
325 > +++ b/emacs/notmuch.el\r
326 > @@ -797,9 +797,8 @@ non-authors is found, assume that all of the authors match."\r
327 >      (notmuch-search-insert-authors format-string (plist-get result :authors)))\r
328 >  \r
329 >     ((string-equal field "tags")\r
330 > -    (let ((tags-str (mapconcat 'identity (plist-get result :tags) " ")))\r
331 > -      (insert (propertize (format format-string tags-str)\r
332 > -                       'face 'notmuch-tag-face))))))\r
333 > +    (let ((tags (plist-get result :tags)))\r
334 > +      (insert (format format-string (notmuch-tag-format-tags tags)))))))\r
335 >  \r
336 >  (defun notmuch-search-show-result (result &optional pos)\r
337 >    "Insert RESULT at POS or the end of the buffer if POS is null."\r