Flat search and threaded views
[notmuch-archives.git] / 42 / 400b4129e44b714ca4b9b2f9494af94bbce106
1 Return-Path: <sojkam1@fel.cvut.cz>\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 A0849431FDD\r
6         for <notmuch@notmuchmail.org>; Fri, 24 Oct 2014 03:57:30 -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: -2.3\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_MED=-2.3] 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 ftc+qzG-5rJh for <notmuch@notmuchmail.org>;\r
16         Fri, 24 Oct 2014 03:57:23 -0700 (PDT)\r
17 Received: from max.feld.cvut.cz (max.feld.cvut.cz [147.32.192.36])\r
18         by olra.theworths.org (Postfix) with ESMTP id 11DC9431FBD\r
19         for <notmuch@notmuchmail.org>; Fri, 24 Oct 2014 03:57:23 -0700 (PDT)\r
20 Received: from localhost (unknown [192.168.200.7])\r
21         by max.feld.cvut.cz (Postfix) with ESMTP id B8B3B3CFEE6;\r
22         Fri, 24 Oct 2014 12:57:21 +0200 (CEST)\r
23 X-Virus-Scanned: IMAP STYX AMAVIS\r
24 Received: from max.feld.cvut.cz ([192.168.200.1])\r
25         by localhost (styx.feld.cvut.cz [192.168.200.7]) (amavisd-new,\r
26         port 10044)\r
27         with ESMTP id 1IznQ5zjB6qN; Fri, 24 Oct 2014 12:57:16 +0200 (CEST)\r
28 Received: from imap.feld.cvut.cz (imap.feld.cvut.cz [147.32.192.34])\r
29         by max.feld.cvut.cz (Postfix) with ESMTP id CE5325CCF00;\r
30         Fri, 24 Oct 2014 12:57:16 +0200 (CEST)\r
31 Received: from wsh by steelpick.2x.cz with local (Exim 4.84)\r
32         (envelope-from <sojkam1@fel.cvut.cz>)\r
33         id 1XhcYW-0004pj-PB; Fri, 24 Oct 2014 12:57:16 +0200\r
34 From: Michal Sojka <sojkam1@fel.cvut.cz>\r
35 To: Mark Walters <markwalters1009@gmail.com>, notmuch@notmuchmail.org\r
36 Subject: Re: [PATCH v3 3/4] cli: Extend the search command for\r
37         --output={sender, recipients}\r
38 In-Reply-To: <87mw8obcob.fsf@qmul.ac.uk>\r
39 References: <87zjd51phx.fsf@steelpick.2x.cz>\r
40         <1413150093-8383-1-git-send-email-sojkam1@fel.cvut.cz>\r
41         <1413150093-8383-4-git-send-email-sojkam1@fel.cvut.cz>\r
42         <87mw8obcob.fsf@qmul.ac.uk>\r
43 User-Agent: Notmuch/0.18.1+101~g56b0ff0 (http://notmuchmail.org) Emacs/24.3.1\r
44         (x86_64-pc-linux-gnu)\r
45 Date: Fri, 24 Oct 2014 12:57:16 +0200\r
46 Message-ID: <87fvedag6r.fsf@steelpick.2x.cz>\r
47 MIME-Version: 1.0\r
48 Content-Type: text/plain; charset=utf-8\r
49 Content-Transfer-Encoding: quoted-printable\r
50 X-BeenThere: notmuch@notmuchmail.org\r
51 X-Mailman-Version: 2.1.13\r
52 Precedence: list\r
53 List-Id: "Use and development of the notmuch mail system."\r
54         <notmuch.notmuchmail.org>\r
55 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
56         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
57 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
58 List-Post: <mailto:notmuch@notmuchmail.org>\r
59 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
60 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
61         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
62 X-List-Received-Date: Fri, 24 Oct 2014 10:57:30 -0000\r
63 \r
64 Hi Mark,\r
65 \r
66 I mostly agree with your points mentioned in this and other your emails.\r
67 I'll prepare v4 based on that.\r
68 \r
69 On Wed, Oct 22 2014, Mark Walters wrote:\r
70 > On Sun, 12 Oct 2014, Michal Sojka <sojkam1@fel.cvut.cz> wrote:\r
71 >> The new outputs allow printing senders, recipients or both of matching\r
72 >> messages. The --output option is converted from "keyword" argument to\r
73 >> "flags" argument, which means that the user can use --output=3Dsender and\r
74 >> --output=3Drecipients simultaneously, to print both. Other combinations\r
75 >> produce an error.\r
76 >>\r
77 >> This code based on a patch from Jani Nikula.\r
78 >> ---\r
79 >>  completion/notmuch-completion.bash |   2 +-\r
80 >>  completion/notmuch-completion.zsh  |   3 +-\r
81 >>  doc/man1/notmuch-search.rst        |  22 +++++++-\r
82 >>  notmuch-search.c                   | 110 ++++++++++++++++++++++++++++++=\r
83 ++++---\r
84 >>  test/T090-search-output.sh         |  64 +++++++++++++++++++++\r
85 >>  5 files changed, 189 insertions(+), 12 deletions(-)\r
86 >>\r
87 >> diff --git a/completion/notmuch-completion.bash b/completion/notmuch-com=\r
88 pletion.bash\r
89 >> index 0571dc9..cfbd389 100644\r
90 >> --- a/completion/notmuch-completion.bash\r
91 >> +++ b/completion/notmuch-completion.bash\r
92 >> @@ -294,7 +294,7 @@ _notmuch_search()\r
93 >>          return\r
94 >>          ;;\r
95 >>      --output)\r
96 >> -        COMPREPLY=3D( $( compgen -W "summary threads messages files tags" =\r
97 -- "${cur}" ) )\r
98 >> +        COMPREPLY=3D( $( compgen -W "summary threads messages files tags s=\r
99 ender recipients" -- "${cur}" ) )\r
100 >>          return\r
101 >>          ;;\r
102 >>      --sort)\r
103 >> diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-comp=\r
104 letion.zsh\r
105 >> index 67a9aba..3e52a00 100644\r
106 >> --- a/completion/notmuch-completion.zsh\r
107 >> +++ b/completion/notmuch-completion.zsh\r
108 >> @@ -52,7 +52,8 @@ _notmuch_search()\r
109 >>    _arguments -s : \\r
110 >>      '--max-threads=3D[display only the first x threads from the search =\r
111 results]:number of threads to show: ' \\r
112 >>      '--first=3D[omit the first x threads from the search results]:numbe=\r
113 r of threads to omit: ' \\r
114 >> -    '--sort=3D[sort results]:sorting:((newest-first\:"reverse chronolog=\r
115 ical order" oldest-first\:"chronological order"))'\r
116 >> +    '--sort=3D[sort results]:sorting:((newest-first\:"reverse chronolog=\r
117 ical order" oldest-first\:"chronological order"))' \\r
118 >> +    '--output=3D[select what to output]:output:((summary threads messag=\r
119 es files tags sender recipients))'\r
120 >>  }\r
121 >>\r
122 >>  _notmuch()\r
123 >> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst\r
124 >> index 90160f2..c9d38b1 100644\r
125 >> --- a/doc/man1/notmuch-search.rst\r
126 >> +++ b/doc/man1/notmuch-search.rst\r
127 >> @@ -35,7 +35,7 @@ Supported options for **search** include\r
128 >>          intended for programs that invoke **notmuch(1)** internally. If\r
129 >>          omitted, the latest supported version will be used.\r
130 >>\r
131 >> -    ``--output=3D(summary|threads|messages|files|tags)``\r
132 >> +    ``--output=3D(summary|threads|messages|files|tags|sender|recipients=\r
133 )``\r
134 >>\r
135 >>          **summary**\r
136 >>              Output a summary of each thread with any message matching\r
137 >> @@ -78,6 +78,26 @@ Supported options for **search** include\r
138 >>              by null characters (--format=3Dtext0), as a JSON array\r
139 >>              (--format=3Djson), or as an S-Expression list (--format=3Ds=\r
140 exp).\r
141 >>\r
142 >> +    **sender**\r
143 >> +            Output all addresses from the *From* header that appear on\r
144 >> +            any message matching the search terms, either one per line\r
145 >> +            (--format=3Dtext), separated by null characters\r
146 >> +            (--format=3Dtext0), as a JSON array (--format=3Djson), or as\r
147 >> +            an S-Expression list (--format=3Dsexp).\r
148 >> +\r
149 >> +        Note: Searching for **sender** should be much faster than\r
150 >> +        searching for **recipients**, because sender addresses are\r
151 >> +        cached directly in the database whereas other addresses\r
152 >> +        need to be fetched from message files.\r
153 >> +\r
154 >> +    **recipients**\r
155 >> +            Like **sender** but for addresses from *To*, *Cc* and\r
156 >> +        *Bcc* headers.\r
157 >> +\r
158 >> +    This option can be given multiple times to combine different\r
159 >> +    outputs. Curently, this is only supported for **sender** and\r
160 >> +    **recipients** outputs.\r
161 >> +\r
162 >>      ``--sort=3D``\ (**newest-first**\ \|\ **oldest-first**)\r
163 >>          This option can be used to present results in either\r
164 >>          chronological order (**oldest-first**) or reverse chronological\r
165 >> diff --git a/notmuch-search.c b/notmuch-search.c\r
166 >> index 5ac2a26..74588f8 100644\r
167 >> --- a/notmuch-search.c\r
168 >> +++ b/notmuch-search.c\r
169 >> @@ -23,11 +23,14 @@\r
170 >>  #include "string-util.h"\r
171 >>\r
172 >>  typedef enum {\r
173 >> -    OUTPUT_SUMMARY,\r
174 >> -    OUTPUT_THREADS,\r
175 >> -    OUTPUT_MESSAGES,\r
176 >> -    OUTPUT_FILES,\r
177 >> -    OUTPUT_TAGS\r
178 >> +    OUTPUT_SUMMARY  =3D 1 << 0,\r
179 >> +    OUTPUT_THREADS  =3D 1 << 1,\r
180 >> +    OUTPUT_MESSAGES =3D 1 << 2,\r
181 >> +    OUTPUT_FILES    =3D 1 << 3,\r
182 >> +    OUTPUT_TAGS             =3D 1 << 4,\r
183 >> +    OUTPUT_SENDER   =3D 1 << 5,\r
184 >> +    OUTPUT_RECIPIENTS       =3D 1 << 6,\r
185 >> +    OUTPUT_ADDRESSES        =3D OUTPUT_SENDER | OUTPUT_RECIPIENTS,\r
186 >\r
187 > I think I would drop the OUTPUT_ADDRESSES enum as the parser no longer\r
188 > uses it (and replace the one use by OUTPUT_SENDER | OUTPUT_RECIPIENTS\r
189 > below).\r
190 \r
191 As mentioned elsewhere, this is required to suppress the following warning.\r
192 \r
193 notmuch-search.c:634:5: warning: case value =E2=80=9896=E2=80=99 not in enu=\r
194 merated type =E2=80=98output_t=E2=80=99 [-Wswitch]\r
195 \r
196 >\r
197 >>  } output_t;\r
198 >>\r
199 >>  typedef struct {\r
200 >> @@ -220,6 +223,67 @@ do_search_threads (search_options_t *o)\r
201 >>      return 0;\r
202 >>  }\r
203 >>\r
204 >> +static void\r
205 >> +print_address_list (const search_options_t *o, InternetAddressList *lis=\r
206 t)\r
207 >> +{\r
208 >> +    InternetAddress *address;\r
209 >> +    int i;\r
210 >> +\r
211 >> +    for (i =3D 0; i < internet_address_list_length (list); i++) {\r
212 >> +    address =3D internet_address_list_get_address (list, i);\r
213 >> +    if (INTERNET_ADDRESS_IS_GROUP (address)) {\r
214 >> +        InternetAddressGroup *group;\r
215 >> +        InternetAddressList *group_list;\r
216 >> +\r
217 >> +        group =3D INTERNET_ADDRESS_GROUP (address);\r
218 >> +        group_list =3D internet_address_group_get_members (group);\r
219 >> +        if (group_list =3D=3D NULL)\r
220 >> +            continue;\r
221 >> +\r
222 >> +        print_address_list (o, group_list);\r
223 >> +    } else {\r
224 >> +        InternetAddressMailbox *mailbox;\r
225 >> +        const char *name;\r
226 >> +        const char *addr;\r
227 >> +        char *full_address;\r
228 >> +\r
229 >> +        mailbox =3D INTERNET_ADDRESS_MAILBOX (address);\r
230 >> +\r
231 >> +        name =3D internet_address_get_name (address);\r
232 >> +        addr =3D internet_address_mailbox_get_addr (mailbox);\r
233 >> +\r
234 >> +        if (name && *name)\r
235 >> +            full_address =3D talloc_asprintf (o->format, "%s <%s>", name, addr);\r
236 >> +        else\r
237 >> +            full_address =3D talloc_strdup (o->format, addr);\r
238 >> +\r
239 >> +        if (!full_address) {\r
240 >> +            fprintf (stderr, "Error: out of memory\n");\r
241 >> +            break;\r
242 >> +        }\r
243 >> +        o->format->string (o->format, full_address);\r
244 >> +        o->format->separator (o->format);\r
245 >> +\r
246 >> +        talloc_free (full_address);\r
247 >> +    }\r
248 >> +    }\r
249 >> +}\r
250 >> +\r
251 >> +static void\r
252 >> +print_address_string (const search_options_t *o, const char *recipients)\r
253 >> +{\r
254 >> +    InternetAddressList *list;\r
255 >> +\r
256 >> +    if (recipients =3D=3D NULL)\r
257 >> +    return;\r
258 >> +\r
259 >> +    list =3D internet_address_list_parse_string (recipients);\r
260 >> +    if (list =3D=3D NULL)\r
261 >> +    return;\r
262 >> +\r
263 >> +    print_address_list (o, list);\r
264 >> +}\r
265 >> +\r
266 >>  static int\r
267 >>  do_search_messages (search_options_t *o)\r
268 >>  {\r
269 >> @@ -266,11 +330,29 @@ do_search_messages (search_options_t *o)\r
270 >>\r
271 >>          notmuch_filenames_destroy( filenames );\r
272 >>\r
273 >> -    } else { /* output =3D=3D OUTPUT_MESSAGES */\r
274 >> +    } else if (o->output =3D=3D OUTPUT_MESSAGES) {\r
275 >>          format->set_prefix (format, "id");\r
276 >>          format->string (format,\r
277 >>                          notmuch_message_get_message_id (message));\r
278 >>          format->separator (format);\r
279 >> +    } else {\r
280 >> +        if (o->output & OUTPUT_SENDER) {\r
281 >> +            const char *addrs;\r
282 >> +\r
283 >> +            addrs =3D notmuch_message_get_header (message, "from");\r
284 >> +            print_address_string (o, addrs);\r
285 >> +        }\r
286 >> +\r
287 >> +        if (o->output & OUTPUT_RECIPIENTS) {\r
288 >> +            const char *hdrs[] =3D { "to", "cc", "bcc" };\r
289 >> +            const char *addrs;\r
290 >> +            size_t j;\r
291 >> +\r
292 >> +            for (j =3D 0; j < ARRAY_SIZE (hdrs); j++) {\r
293 >> +                addrs =3D notmuch_message_get_header (message, hdrs[j]);\r
294 >> +                print_address_string (o, addrs);\r
295 >> +            }\r
296 >> +        }\r
297 >>      }\r
298 >>\r
299 >>      notmuch_message_destroy (message);\r
300 >> @@ -337,7 +419,7 @@ notmuch_search_command (notmuch_config_t *config, in=\r
301 t argc, char *argv[])\r
302 >>      notmuch_database_t *notmuch;\r
303 >>      search_options_t o =3D {\r
304 >>      .sort =3D NOTMUCH_SORT_NEWEST_FIRST,\r
305 >> -    .output =3D OUTPUT_SUMMARY,\r
306 >> +    .output =3D 0,\r
307 >>      .offset =3D 0,\r
308 >>      .limit =3D -1, /* unlimited */\r
309 >>      .dupe =3D -1,\r
310 >\r
311 > It is slightly unfortunate that all the other defaults are defined here\r
312 > but the default output is after the option parsing but I can't see a\r
313 > nice way round that. I only mention it in case you (or anyone else) can\r
314 > see a nice way round this.\r
315 \r
316 Solution would to have --output as NOTMUCH_OPT_KEYWORD rather than\r
317 NOTMUCH_OPT_KEYWORD_FLAGS as it was in v2.\r
318 \r
319 >\r
320 >> @@ -366,10 +448,12 @@ notmuch_search_command (notmuch_config_t *config, =\r
321 int argc, char *argv[])\r
322 >>                                { "text0", NOTMUCH_FORMAT_TEXT0 },\r
323 >>                                { 0, 0 } } },\r
324 >>      { NOTMUCH_OPT_INT, &notmuch_format_version, "format-version", 0, 0 },\r
325 >> -    { NOTMUCH_OPT_KEYWORD, &o.output, "output", 'o',\r
326 >> +    { NOTMUCH_OPT_KEYWORD_FLAGS, &o.output, "output", 'o',\r
327 >>        (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },\r
328 >>                                { "threads", OUTPUT_THREADS },\r
329 >>                                { "messages", OUTPUT_MESSAGES },\r
330 >> +                              { "sender", OUTPUT_SENDER },\r
331 >> +                              { "recipients", OUTPUT_RECIPIENTS },\r
332 >>                                { "files", OUTPUT_FILES },\r
333 >>                                { "tags", OUTPUT_TAGS },\r
334 >>                                { 0, 0 } } },\r
335 >> @@ -389,6 +473,9 @@ notmuch_search_command (notmuch_config_t *config, in=\r
336 t argc, char *argv[])\r
337 >>      if (opt_index < 0)\r
338 >>      return EXIT_FAILURE;\r
339 >>\r
340 >> +    if (! o.output)\r
341 >> +    o.output =3D OUTPUT_SUMMARY;\r
342 >> +\r
343 >>      switch (format_sel) {\r
344 >>      case NOTMUCH_FORMAT_TEXT:\r
345 >>      o.format =3D sprinter_text_create (config, stdout);\r
346 >> @@ -455,18 +542,23 @@ notmuch_search_command (notmuch_config_t *config, =\r
347 int argc, char *argv[])\r
348 >>      }\r
349 >>\r
350 >>      switch (o.output) {\r
351 >> -    default:\r
352 >>      case OUTPUT_SUMMARY:\r
353 >>      case OUTPUT_THREADS:\r
354 >>      ret =3D do_search_threads (&o);\r
355 >>      break;\r
356 >>      case OUTPUT_MESSAGES:\r
357 >> +    case OUTPUT_SENDER:\r
358 >> +    case OUTPUT_RECIPIENTS:\r
359 >> +    case OUTPUT_ADDRESSES:\r
360 >\r
361 > This is the one use of OUTPUT_ADDRESSES that I would replace with\r
362 > OUTPUT_SENDER | OUTPUT_RECIPIENTS\r
363 \r
364 See above.\r
365 \r
366 -Michal\r