Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 66 / 0ef94825bf15ff9917c8618cb504e0d80c3e58
1 Return-Path: <dmitry.kurochkin@gmail.com>\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 18AE1431FD2\r
6         for <notmuch@notmuchmail.org>; Sun, 29 Jan 2012 17:33:57 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.799\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.799 tagged_above=-999 required=5\r
12         tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
13         FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7] 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 xJ0H9W9noKAI for <notmuch@notmuchmail.org>;\r
17         Sun, 29 Jan 2012 17:33:55 -0800 (PST)\r
18 Received: from mail-bk0-f53.google.com (mail-bk0-f53.google.com\r
19         [209.85.214.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 349D9431FBC\r
22         for <notmuch@notmuchmail.org>; Sun, 29 Jan 2012 17:33:55 -0800 (PST)\r
23 Received: by bke11 with SMTP id 11so933769bke.26\r
24         for <notmuch@notmuchmail.org>; Sun, 29 Jan 2012 17:33:53 -0800 (PST)\r
25 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma;\r
26         h=from:to:cc:subject:in-reply-to:references:user-agent:date\r
27         :message-id:mime-version:content-type;\r
28         bh=VUjLvaWi8mWuLvqUj1xpTKot7UV3F0gzEz05pHA6MPk=;\r
29         b=pByzmrefDaF1Ti0hZZ2KIh5MBpM30RFsF471GJcg50ORLplS2EYOGEjIurj9DZIv4w\r
30         /0i4T8r3Z+3ioMQyyQpkWBdJ/tJsThtJfCQTeyUOO+AlH8wvo3v30XkLFGC3YRSPTBlh\r
31         lyTRx1tugdYh8Viu2/AIYY9wJ0vSQAphN/LFU=\r
32 Received: by 10.204.153.27 with SMTP id i27mr7672093bkw.81.1327887233783;\r
33         Sun, 29 Jan 2012 17:33:53 -0800 (PST)\r
34 Received: from localhost ([91.144.186.21]) by mx.google.com with ESMTPS id\r
35         ci12sm33968605bkb.13.2012.01.29.17.33.52\r
36         (version=TLSv1/SSLv3 cipher=OTHER);\r
37         Sun, 29 Jan 2012 17:33:53 -0800 (PST)\r
38 From: Dmitry Kurochkin <dmitry.kurochkin@gmail.com>\r
39 To: Austin Clements <amdragon@MIT.EDU>\r
40 Subject: Re: [PATCH 3/6] emacs: make "+" and "-" tagging operations more\r
41  robust\r
42 In-Reply-To: <20120129225710.GG17991@mit.edu>\r
43 References: <1327725684-5887-1-git-send-email-dmitry.kurochkin@gmail.com>\r
44         <1327725684-5887-3-git-send-email-dmitry.kurochkin@gmail.com>\r
45         <20120129225710.GG17991@mit.edu>\r
46 User-Agent: Notmuch/0.11+134~g7ddba9d (http://notmuchmail.org) Emacs/23.3.1\r
47         (x86_64-pc-linux-gnu)\r
48 Date: Mon, 30 Jan 2012 05:32:43 +0400\r
49 Message-ID: <8762fu7z5w.fsf@gmail.com>\r
50 MIME-Version: 1.0\r
51 Content-Type: text/plain; charset=us-ascii\r
52 Cc: notmuch@notmuchmail.org\r
53 X-BeenThere: notmuch@notmuchmail.org\r
54 X-Mailman-Version: 2.1.13\r
55 Precedence: list\r
56 List-Id: "Use and development of the notmuch mail system."\r
57         <notmuch.notmuchmail.org>\r
58 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
59         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
60 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
61 List-Post: <mailto:notmuch@notmuchmail.org>\r
62 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
63 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
64         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
65 X-List-Received-Date: Mon, 30 Jan 2012 01:33:57 -0000\r
66 \r
67 Hi Austin.\r
68 \r
69 The below changes will be send as v2 soon.\r
70 \r
71 On Sun, 29 Jan 2012 17:57:10 -0500, Austin Clements <amdragon@MIT.EDU> wrote:\r
72 > I'm looking forward to having this.  I think it'll streamline tagging\r
73 > operations.\r
74\r
75 > Quoth Dmitry Kurochkin on Jan 28 at  8:41 am:\r
76 > > Before the change, "+" and "-" tagging operations in notmuch-search\r
77 > > and notmuch-show views accepted only a single tag.  The patch makes\r
78 > > them use the recently added `notmuch-select-tags-with-completion'\r
79 > > function, which allows to enter multiple tags with "+" and "-"\r
80 > > prefixes.  So after the change, "+" and "-" bindings allow to both add\r
81 > > and remove multiple tags.  The only difference between "+" and "-" is\r
82 > > the minibuffer initial input ("+" and "-" respectively).\r
83\r
84 > This patch was a little difficult to review because it was largish and\r
85 > the diff happened to contain a bunch of forward references.  If it's\r
86 > convenient (don't bother if it's not), could you divide up the next\r
87 > version a little?  Something simple like sending the show changes as a\r
88 > separate patch would probably make it a lot easier.\r
89\r
90 \r
91 done\r
92 \r
93 > > ---\r
94 > >  emacs/notmuch-show.el |   65 +++++++------------\r
95 > >  emacs/notmuch.el      |  165 +++++++++++++++++++++++++------------------------\r
96 > >  2 files changed, 107 insertions(+), 123 deletions(-)\r
97 > > \r
98 > > diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
99 > > index 84ac624..03eadfb 100644\r
100 > > --- a/emacs/notmuch-show.el\r
101 > > +++ b/emacs/notmuch-show.el\r
102 > > @@ -38,8 +38,9 @@\r
103 > >  \r
104 > >  (declare-function notmuch-call-notmuch-process "notmuch" (&rest args))\r
105 > >  (declare-function notmuch-fontify-headers "notmuch" nil)\r
106 > > -(declare-function notmuch-select-tag-with-completion "notmuch" (prompt &rest search-terms))\r
107 > > +(declare-function notmuch-select-tags-with-completion "notmuch" (&optional initial-input &rest search-terms))\r
108 > >  (declare-function notmuch-search-show-thread "notmuch" nil)\r
109 > > +(declare-function notmuch-update-tags "notmuch" (current-tags changed-tags))\r
110 > >  \r
111 > >  (defcustom notmuch-message-headers '("Subject" "To" "Cc" "Date")\r
112 > >    "Headers that should be shown in a message, in this order.\r
113 > > @@ -1267,7 +1268,7 @@ Some useful entries are:\r
114 > >  \r
115 > >  (defun notmuch-show-mark-read ()\r
116 > >    "Mark the current message as read."\r
117 > > -  (notmuch-show-remove-tag "unread"))\r
118 > > +  (notmuch-show-tag-message "-unread"))\r
119 > >  \r
120 > >  ;; Functions for getting attributes of several messages in the current\r
121 > >  ;; thread.\r
122 > > @@ -1470,51 +1471,33 @@ than only the current message."\r
123 > >         (message (format "Command '%s' exited abnormally with code %d"\r
124 > >                          shell-command exit-code))))))))\r
125 > >  \r
126 > > -(defun notmuch-show-add-tags-worker (current-tags add-tags)\r
127 > > -  "Add to `current-tags' with any tags from `add-tags' not\r
128 > > -currently present and return the result."\r
129 > > -  (let ((result-tags (copy-sequence current-tags)))\r
130 > > -    (mapc (lambda (add-tag)\r
131 > > -       (unless (member add-tag current-tags)\r
132 > > -         (setq result-tags (push add-tag result-tags))))\r
133 > > -       add-tags)\r
134 > > -    (sort result-tags 'string<)))\r
135 > > -\r
136 > > -(defun notmuch-show-del-tags-worker (current-tags del-tags)\r
137 > > -  "Remove any tags in `del-tags' from `current-tags' and return\r
138 > > -the result."\r
139 > > -  (let ((result-tags (copy-sequence current-tags)))\r
140 > > -    (mapc (lambda (del-tag)\r
141 > > -       (setq result-tags (delete del-tag result-tags)))\r
142 > > -     del-tags)\r
143 > > -    result-tags))\r
144 > > -\r
145 > > -(defun notmuch-show-add-tag (&rest toadd)\r
146 > > -  "Add a tag to the current message."\r
147 > > -  (interactive\r
148 > > -   (list (notmuch-select-tag-with-completion "Tag to add: ")))\r
149 > > +(defun notmuch-show-tag-message (&rest changed-tags)\r
150 > > +  "Change tags for the current message.\r
151 > >  \r
152 > > +`Changed-tags' is a list of tag operations for \"notmuch tag\",\r
153 > > +i.e. a list of tags to change with '+' and '-' prefixes."\r
154\r
155 > Ticks in a docstring indicate functions (and will be hyperlinked as\r
156 > such by describe-function).  Typically, argument names are indicated\r
157 > by writing them in all caps.\r
158\r
159 \r
160 Thanks for the explanation.  Fixed.\r
161 \r
162 > Also, it probably makes more sense to reference `notmuch-tag' than\r
163 > "notmuch tag", since this is Lisp land (and, since that will be\r
164 > helpfully hyperlinked, you probably don't need to explain changed-tags\r
165 > here).\r
166\r
167 \r
168 Makes sense, done.\r
169 \r
170 > >    (let* ((current-tags (notmuch-show-get-tags))\r
171 > > -    (new-tags (notmuch-show-add-tags-worker current-tags toadd)))\r
172 > > -\r
173 > > +    (new-tags (notmuch-update-tags current-tags changed-tags)))\r
174 > >      (unless (equal current-tags new-tags)\r
175 > > -      (apply 'notmuch-tag (notmuch-show-get-message-id)\r
176 > > -        (mapcar (lambda (s) (concat "+" s)) toadd))\r
177 > > +      (apply 'notmuch-tag (notmuch-show-get-message-id) changed-tags)\r
178 > >        (notmuch-show-set-tags new-tags))))\r
179 > >  \r
180 > > -(defun notmuch-show-remove-tag (&rest toremove)\r
181 > > -  "Remove a tag from the current message."\r
182 > > -  (interactive\r
183 > > -   (list (notmuch-select-tag-with-completion\r
184 > > -     "Tag to remove: " (notmuch-show-get-message-id))))\r
185 > > +(defun notmuch-show-tag (&optional initial-input)\r
186 > > +  "Change tags for the current message, read input from the minibuffer."\r
187 > > +  (interactive)\r
188 > > +  (let ((changed-tags (notmuch-select-tags-with-completion\r
189 > > +                  initial-input (notmuch-show-get-message-id))))\r
190 > > +    (apply 'notmuch-show-tag-message changed-tags)))\r
191 > >  \r
192 > > -  (let* ((current-tags (notmuch-show-get-tags))\r
193 > > -    (new-tags (notmuch-show-del-tags-worker current-tags toremove)))\r
194 > > +(defun notmuch-show-add-tag ()\r
195 > > +  "Same as `notmuch-show-tag' but sets initial input to '+'."\r
196 > > +  (interactive)\r
197 > > +  (notmuch-show-tag "+"))\r
198 > >  \r
199 > > -    (unless (equal current-tags new-tags)\r
200 > > -      (apply 'notmuch-tag (notmuch-show-get-message-id)\r
201 > > -        (mapcar (lambda (s) (concat "-" s)) toremove))\r
202 > > -      (notmuch-show-set-tags new-tags))))\r
203 > > +(defun notmuch-show-remove-tag ()\r
204 > > +  "Same as `notmuch-show-tag' but sets initial input to '-'."\r
205 > > +  (interactive)\r
206 > > +  (notmuch-show-tag "-"))\r
207\r
208 > Should notmuch-show-{add,remove}-tag be considered public functions?\r
209 > Previously, they were amenable to creating bindings for adding or\r
210 > removing individual tags, and I believe people have done this.  If\r
211 > we're okay with breaking backward-compatibility, there should at least\r
212 > be a NEWS item explaining how to convert such custom bindings to use\r
213 > notmuch-show-tag-message.\r
214\r
215 \r
216 I am definitely ok with breaking backward-compatibility here.  NEWS item\r
217 is a good idea, added.\r
218 \r
219 > >  \r
220 > >  (defun notmuch-show-toggle-headers ()\r
221 > >    "Toggle the visibility of the current message headers."\r
222 > > @@ -1559,7 +1542,7 @@ argument, hide all of the messages."\r
223 > >  (defun notmuch-show-archive-thread-internal (show-next)\r
224 > >    ;; Remove the tag from the current set of messages.\r
225 > >    (goto-char (point-min))\r
226 > > -  (loop do (notmuch-show-remove-tag "inbox")\r
227 > > +  (loop do (notmuch-show-tag-message "-inbox")\r
228 > >     until (not (notmuch-show-goto-message-next)))\r
229 > >    ;; Move to the next item in the search results, if any.\r
230 > >    (let ((parent-buffer notmuch-show-parent-buffer))\r
231 > > diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
232 > > index ff46617..24b0ea3 100644\r
233 > > --- a/emacs/notmuch.el\r
234 > > +++ b/emacs/notmuch.el\r
235 > > @@ -76,38 +76,56 @@ For example:\r
236 > >  (defvar notmuch-query-history nil\r
237 > >    "Variable to store minibuffer history for notmuch queries")\r
238 > >  \r
239 > > -(defun notmuch-tag-completions (&optional prefixes search-terms)\r
240 > > -  (let ((tag-list\r
241 > > -    (split-string\r
242 > > -     (with-output-to-string\r
243 > > -       (with-current-buffer standard-output\r
244 > > -         (apply 'call-process notmuch-command nil t\r
245 > > -                nil "search-tags" search-terms)))\r
246 > > -     "\n+" t)))\r
247 > > -    (if (null prefixes)\r
248 > > -   tag-list\r
249 > > -      (apply #'append\r
250 > > -        (mapcar (lambda (tag)\r
251 > > -                  (mapcar (lambda (prefix)\r
252 > > -                            (concat prefix tag)) prefixes))\r
253 > > -                tag-list)))))\r
254 > > +(defun notmuch-tag-completions (&optional search-terms)\r
255 > > +  (split-string\r
256 > > +   (with-output-to-string\r
257 > > +     (with-current-buffer standard-output\r
258 > > +       (apply 'call-process notmuch-command nil t\r
259 > > +         nil "search-tags" search-terms)))\r
260 > > +   "\n+" t))\r
261 > >  \r
262 > >  (defun notmuch-select-tag-with-completion (prompt &rest search-terms)\r
263 > > -  (let ((tag-list (notmuch-tag-completions nil search-terms)))\r
264 > > +  (let ((tag-list (notmuch-tag-completions search-terms)))\r
265 > >      (completing-read prompt tag-list)))\r
266 > >  \r
267 > > -(defun notmuch-select-tags-with-completion (prompt &optional prefixes &rest search-terms)\r
268 > > -  (let ((tag-list (notmuch-tag-completions prefixes search-terms))\r
269 > > -   (crm-separator " ")\r
270 > > -   ;; By default, space is bound to "complete word" function.\r
271 > > -   ;; Re-bind it to insert a space instead.  Note that <tab>\r
272 > > -   ;; still does the completion.\r
273 > > -   (crm-local-completion-map\r
274 > > -    (let ((map (make-sparse-keymap)))\r
275 > > -      (set-keymap-parent map crm-local-completion-map)\r
276 > > -      (define-key map " " 'self-insert-command)\r
277 > > -      map)))\r
278 > > -    (delete "" (completing-read-multiple prompt tag-list))))\r
279 > > +(defun notmuch-select-tags-with-completion (&optional initial-input &rest search-terms)\r
280\r
281 > I don't know if notmuch-select-tags-with-completion is the right name\r
282 > for this now that it hard-codes the +/- prefixes (which seems like the\r
283 > right thing to do, BTW).  Maybe notmuch-read-tags-add-remove?\r
284\r
285 \r
286 How about `notmuch-read-tag-changes'?\r
287 \r
288 > > +  (let* ((add-tag-list (mapcar (apply-partially 'concat "+")\r
289 > > +                          (notmuch-tag-completions)))\r
290 > > +    (remove-tag-list (mapcar (apply-partially 'concat "-")\r
291 > > +                             (notmuch-tag-completions search-terms)))\r
292\r
293 > This will make two calls to notmuch search, but often one will\r
294 > suffice.  It's probably worth optimizing the case were search-terms is\r
295 > nil.\r
296\r
297 \r
298 done\r
299 \r
300 > > +    (tag-list (append add-tag-list remove-tag-list))\r
301 > > +    (crm-separator " ")\r
302 > > +    ;; By default, space is bound to "complete word" function.\r
303 > > +    ;; Re-bind it to insert a space instead.  Note that <tab>\r
304 > > +    ;; still does the completion.\r
305 > > +    (crm-local-completion-map\r
306 > > +     (let ((map (make-sparse-keymap)))\r
307 > > +       (set-keymap-parent map crm-local-completion-map)\r
308 > > +       (define-key map " " 'self-insert-command)\r
309 > > +       map)))\r
310 > > +    (delete "" (completing-read-multiple\r
311 > > +           "Operations (+add -drop): notmuch tag " tag-list nil\r
312\r
313 > I don't think the "notmuch tag" part is necessary.  From the\r
314 > perspective of a person who only uses the Emacs UI, this will be\r
315 > meaningless.  Maybe "Tag changes (+add -drop): " or even just "Tags\r
316 > (+add -drop): " since the "+add -drop" part implies what you're doing.\r
317\r
318 \r
319 Just "tags" looks good to me, changed.\r
320 \r
321 > > +           nil initial-input))))\r
322 > > +\r
323 > > +(defun notmuch-update-tags (current-tags changed-tags)\r
324\r
325 > Maybe just "tags" instead of "current-tags"?  Nothing says they have\r
326 > to be current.  It's just a list of tags.\r
327\r
328 \r
329 changed\r
330 \r
331 > Also, changed-tags makes it sound like a list of tags, which is isn't.\r
332 > Maybe tag-changes?\r
333\r
334 \r
335 Replaced changed-tags with tag-changes everywhere.\r
336 \r
337 > > +  "Update `current-tags' with `changed-tags' and return the result.\r
338 > > +\r
339 > > +`Changed-tags' is a list of tag operations given to \"notmuch\r
340 > > +tag\", i.e. a list of changed tags with '+' and '-' prefixes."\r
341\r
342 > Same comment about ticks and "notmuch tag".\r
343\r
344 > I found this docstring a bit confusing.  I wasn't sure exactly what it\r
345 > meant to "update current-tags with changed-tags" (though replacing\r
346 > changed-tags with tag-changes would probably help).  Plus, this\r
347 > function does not, in fact, update current-tags.  Maybe,\r
348\r
349 >   "Return a copy of TAGS with additions and removals from TAG-CHANGES.\r
350\r
351 > TAG-CHANGES must be a list of tags names, each prefixed with either a\r
352 > \"+\" to indicate the tag should be added to TAGS if not present or a\r
353 > \"-\" to indicate that the tag should be removed from TAGS if\r
354 > present."\r
355\r
356 \r
357 Looks good, using your docstring now.\r
358 \r
359 > > +  (let ((result-tags (copy-sequence current-tags)))\r
360 > > +    (mapc (lambda (changed-tag)\r
361\r
362 > Consider dolist instead of mapc, though this is a matter of taste.  It\r
363 > leads to less indentation (and does have precedent in the notmuch\r
364 > code, though mapc is more common).\r
365\r
366 \r
367 Dolist is definately better here, thanks for the suggestion.\r
368 \r
369 > Too bad Elisp doesn't have fold.\r
370\r
371 \r
372 indeed\r
373 \r
374 > > +       (unless (string= changed-tag "")\r
375 > > +         (let ((op (substring changed-tag 0 1))\r
376 > > +               (tag (substring changed-tag 1)))\r
377 > > +           (cond ((string= op "+")\r
378 > > +                  (unless (member tag result-tags)\r
379 > > +                    (push tag result-tags)))\r
380 > > +                 ((string= op "-")\r
381 > > +                  (setq result-tags (delete tag result-tags)))\r
382 > > +                 (t\r
383 > > +                  (error "Changed tag must be of the form `+this_tag' or `-that_tag'"))))))\r
384\r
385 > I would suggest case instead of cond, but, again, that's a matter of\r
386 > taste.\r
387\r
388 \r
389 Again, `case' is definately better here, changed.\r
390 \r
391 > > +       changed-tags)\r
392 > > +    (sort result-tags 'string<)))\r
393 > >  \r
394 > >  (defun notmuch-foreach-mime-part (function mm-handle)\r
395 > >    (cond ((stringp (car mm-handle))\r
396 > > @@ -447,6 +465,10 @@ Complete list of currently available key bindings:\r
397 > >    "Return a list of threads for the current region"\r
398 > >    (notmuch-search-properties-in-region 'notmuch-search-thread-id beg end))\r
399 > >  \r
400 > > +(defun notmuch-search-find-thread-id-region-search (beg end)\r
401 > > +  "Return a search string for threads for the current region"\r
402 > > +  (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or "))\r
403 > > +\r
404 > >  (defun notmuch-search-find-authors ()\r
405 > >    "Return the authors for the current thread"\r
406 > >    (get-text-property (point) 'notmuch-search-authors))\r
407 > > @@ -590,74 +612,55 @@ the messages that were tagged"\r
408 > >     (forward-line 1))\r
409 > >        output)))\r
410 > >  \r
411 > > -(defun notmuch-search-add-tag-thread (tag)\r
412 > > -  (notmuch-search-add-tag-region tag (point) (point)))\r
413 > > +(defun notmuch-search-tag-thread (&rest tags)\r
414 > > +  "Change tags for the currently selected thread.\r
415 > >  \r
416 > > -(defun notmuch-search-add-tag-region (tag beg end)\r
417 > > -  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or ")))\r
418 > > -    (notmuch-tag search-id-string (concat "+" tag))\r
419 > > -    (save-excursion\r
420 > > -      (let ((last-line (line-number-at-pos end))\r
421 > > -       (max-line (- (line-number-at-pos (point-max)) 2)))\r
422 > > -   (goto-char beg)\r
423 > > -   (while (<= (line-number-at-pos) (min last-line max-line))\r
424 > > -     (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<)))\r
425 > > -     (forward-line))))))\r
426 > > +See `notmuch-search-tag-region' for details."\r
427 > > +  (apply 'notmuch-search-tag-region (point) (point) tags))\r
428 > >  \r
429 > > -(defun notmuch-search-remove-tag-thread (tag)\r
430 > > -  (notmuch-search-remove-tag-region tag (point) (point)))\r
431 > > +(defun notmuch-search-tag-region (beg end &rest tags)\r
432 > > +  "Change tags for threads in the given region.\r
433 > >  \r
434 > > -(defun notmuch-search-remove-tag-region (tag beg end)\r
435 > > -  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or ")))\r
436 > > -    (notmuch-tag search-id-string (concat "-" tag))\r
437 > > +`Tags' is a list of tag operations for \"notmuch tag\", i.e. a\r
438 > > +list of tags to change with '+' and '-' prefixes.  The tags are\r
439 > > +added or removed for all threads in the region from `beg' to\r
440 > > +`end'."\r
441\r
442 > Same comment about ticks and "notmuch tag".\r
443\r
444 \r
445 fixed\r
446 \r
447 > > +  (let ((search-string (notmuch-search-find-thread-id-region-search beg end)))\r
448 > > +    (apply 'notmuch-tag search-string tags)\r
449 > >      (save-excursion\r
450 > >        (let ((last-line (line-number-at-pos end))\r
451 > >         (max-line (- (line-number-at-pos (point-max)) 2)))\r
452 > >     (goto-char beg)\r
453 > >     (while (<= (line-number-at-pos) (min last-line max-line))\r
454 > > -     (notmuch-search-set-tags (delete tag (notmuch-search-get-tags)))\r
455 > > +     (notmuch-search-set-tags\r
456 > > +      (notmuch-update-tags (notmuch-search-get-tags) tags))\r
457 > >       (forward-line))))))\r
458 > >  \r
459 > > -(defun notmuch-search-add-tag (tag)\r
460 > > -  "Add a tag to the currently selected thread or region.\r
461 > > -\r
462 > > -The tag is added to all messages in the currently selected thread\r
463 > > -or threads in the current region."\r
464 > > -  (interactive\r
465 > > -   (list (notmuch-select-tag-with-completion "Tag to add: ")))\r
466 > > -  (save-excursion\r
467 > > -    (if (region-active-p)\r
468 > > -   (let* ((beg (region-beginning))\r
469 > > -          (end (region-end)))\r
470 > > -     (notmuch-search-add-tag-region tag beg end))\r
471 > > -      (notmuch-search-add-tag-thread tag))))\r
472 > > -\r
473 > > -(defun notmuch-search-remove-tag (tag)\r
474 > > -  "Remove a tag from the currently selected thread or region.\r
475 > > +(defun notmuch-search-tag (&optional initial-input)\r
476 > > +  "Change tags for the currently selected thread or region."\r
477 > > +  (interactive)\r
478 > > +  (let* ((beg (if (region-active-p) (region-beginning) (point)))\r
479 > > +    (end (if (region-active-p) (region-end) (point)))\r
480\r
481 > While you're in here, these should probably be `use-region-p'.\r
482\r
483 \r
484 Looks like you are right.  But I think this should be a separate patch.\r
485 I will provide a patch for this after this series is pushed.\r
486 \r
487 Regards,\r
488   Dmitry\r
489 \r
490 > > +    (search-string (notmuch-search-find-thread-id-region-search beg end))\r
491 > > +    (tags (notmuch-select-tags-with-completion initial-input search-string)))\r
492 > > +    (apply 'notmuch-search-tag-region beg end tags)))\r
493 > > +\r
494 > > +(defun notmuch-search-add-tag ()\r
495 > > +  "Same as `notmuch-search-tag' but sets initial input to '+'."\r
496 > > +  (interactive)\r
497 > > +  (notmuch-search-tag "+"))\r
498 > >  \r
499 > > -The tag is removed from all messages in the currently selected\r
500 > > -thread or threads in the current region."\r
501 > > -  (interactive\r
502 > > -   (list (notmuch-select-tag-with-completion\r
503 > > -     "Tag to remove: "\r
504 > > -     (if (region-active-p)\r
505 > > -         (mapconcat 'identity\r
506 > > -                    (notmuch-search-find-thread-id-region (region-beginning) (region-end))\r
507 > > -                    " ")\r
508 > > -       (notmuch-search-find-thread-id)))))\r
509 > > -  (save-excursion\r
510 > > -    (if (region-active-p)\r
511 > > -   (let* ((beg (region-beginning))\r
512 > > -          (end (region-end)))\r
513 > > -     (notmuch-search-remove-tag-region tag beg end))\r
514 > > -      (notmuch-search-remove-tag-thread tag))))\r
515 > > +(defun notmuch-search-remove-tag ()\r
516 > > +  "Same as `notmuch-search-tag' but sets initial input to '-'."\r
517 > > +  (interactive)\r
518 > > +  (notmuch-search-tag "-"))\r
519 > >  \r
520 > >  (defun notmuch-search-archive-thread ()\r
521 > >    "Archive the currently selected thread (remove its \"inbox\" tag).\r
522 > >  \r
523 > >  This function advances the next thread when finished."\r
524 > >    (interactive)\r
525 > > -  (notmuch-search-remove-tag-thread "inbox")\r
526 > > +  (notmuch-search-tag-thread "-inbox")\r
527 > >    (notmuch-search-next-thread))\r
528 > >  \r
529 > >  (defvar notmuch-search-process-filter-data nil\r
530 > > @@ -893,9 +896,7 @@ will prompt for tags to be added or removed. Tags prefixed with\r
531 > >  Each character of the tag name may consist of alphanumeric\r
532 > >  characters as well as `_.+-'.\r
533 > >  "\r
534 > > -  (interactive (notmuch-select-tags-with-completion\r
535 > > -           "Operations (+add -drop): notmuch tag "\r
536 > > -           '("+" "-")))\r
537 > > +  (interactive (notmuch-select-tags-with-completion))\r
538 > >    (apply 'notmuch-tag notmuch-search-query-string actions))\r
539 > >  \r
540 > >  (defun notmuch-search-buffer-title (query)\r