notmuch.el: add functionality in notmuch search mode to add or remove tags by region
authorJesse Rosenthal <jrosenthal@jhu.edu>
Wed, 17 Feb 2010 00:07:40 +0000 (19:07 -0500)
committerCarl Worth <cworth@cworth.org>
Wed, 7 Apr 2010 20:10:36 +0000 (13:10 -0700)
This patch adds `-region' versions of the `notmuch-search-' commands to find
properties. It also splits up  `notmuch-add/remove-tags' into both a
`-thread' and a `-region' version. (This makes us modify
`notmuch-search-archive-thread' to use the
`notmuch-search-remove-tag-thread' function, instead of
`notmuch-search-remove-tag', for consistency.) The add/remove-tag command
called by pressing `+' or `-' will then choose accordingly, based on whether
region is active.

This version fixes a couple of errors in the first version, which led to
incorrect marking of some tags in the search view (though the actual
tagging was still correct). It's also based on current master.

I'm not sure any more if region selection is actually the correct way to
do this, or if a mutt-style message-marking method would be better. But
I didn't want a buggy incorrect version out there.

emacs/notmuch.el

index 4bc429461c599e102111d8bfee8cbae36ea3271a..64f3e3da8edcf0d8222d8466472905aa22d6fbb9 100644 (file)
@@ -396,18 +396,41 @@ Complete list of currently available key bindings:
   (set (make-local-variable 'font-lock-defaults)
          '(notmuch-search-font-lock-keywords t)))
 
+(defun notmuch-search-properties-in-region (property beg end)
+  (save-excursion
+    (let ((output nil)
+         (last-line (line-number-at-pos end)))
+      (goto-char beg)
+      (beginning-of-line)
+      (while (<= (line-number-at-pos) last-line)
+       (setq output (cons (get-text-property (point) property) output))
+       (forward-line 1))
+      output)))
+
 (defun notmuch-search-find-thread-id ()
   "Return the thread for the current thread"
   (get-text-property (point) 'notmuch-search-thread-id))
 
+(defun notmuch-search-find-thread-id-region (beg end)
+  "Return a list of threads for the current region"
+  (notmuch-search-properties-in-region 'notmuch-search-thread-id beg end))
+
 (defun notmuch-search-find-authors ()
   "Return the authors for the current thread"
   (get-text-property (point) 'notmuch-search-authors))
 
+(defun notmuch-search-find-authors-region (beg end)
+  "Return a list of authors for the current region"
+  (notmuch-search-properties-in-region 'notmuch-search-authors beg end))
+
 (defun notmuch-search-find-subject ()
   "Return the subject for the current thread"
   (get-text-property (point) 'notmuch-search-subject))
 
+(defun notmuch-search-find-subject-region (beg end)
+  "Return a list of authors for the current region"
+  (notmuch-search-properties-in-region 'notmuch-search-subject beg end))
+
 (defun notmuch-search-show-thread ()
   "Display the currently selected thread."
   (interactive)
@@ -471,31 +494,85 @@ and will also appear in a buffer named \"*Notmuch errors*\"."
       (let ((end (- (point) 1)))
        (split-string (buffer-substring beg end))))))
 
+(defun notmuch-search-get-tags-region (beg end)
+  (save-excursion
+    (let ((output nil)
+         (last-line (line-number-at-pos end)))
+      (goto-char beg)
+      (while (<= (line-number-at-pos) last-line)
+       (setq output (append output (notmuch-search-get-tags)))
+       (forward-line 1))
+      output)))
+
+(defun notmuch-search-add-tag-thread (tag)
+  (notmuch-call-notmuch-process "tag" (concat "+" tag) (notmuch-search-find-thread-id))
+  (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<))))
+
+(defun notmuch-search-add-tag-region (tag beg end)
+  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or ")))
+    (notmuch-call-notmuch-process "tag" (concat "+" tag) search-id-string)
+    (save-excursion
+      (let ((last-line (line-number-at-pos end)))
+       (goto-char beg)
+       (while (<= (line-number-at-pos) last-line)
+         (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<)))
+         (forward-line))))))
+
+(defun notmuch-search-remove-tag-thread (tag)
+  (notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id))
+  (notmuch-search-set-tags (delete tag (notmuch-search-get-tags))))
+
+(defun notmuch-search-remove-tag-region (tag beg end)
+  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or ")))
+    (notmuch-call-notmuch-process "tag" (concat "-" tag) search-id-string)
+    (save-excursion
+      (let ((last-line (line-number-at-pos end)))
+       (goto-char beg)
+       (while (<= (line-number-at-pos) last-line)
+         (notmuch-search-set-tags (delete tag (notmuch-search-get-tags)))
+         (forward-line))))))
+
+
 (defun notmuch-search-add-tag (tag)
-  "Add a tag to the currently selected thread.
+  "Add a tag to the currently selected thread or region.
 
-The tag is added to messages in the currently selected thread
-which match the current search terms."
+The tag is added to messages in the currently selected thread or
+region which match the current search terms."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to add: ")))
-  (notmuch-call-notmuch-process "tag" (concat "+" tag) (notmuch-search-find-thread-id))
-  (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<))))
+  (save-excursion
+    (if (region-active-p)
+       (let* ((beg (region-beginning))
+              (end (region-end)))
+         (notmuch-search-add-tag-region tag beg end))
+      (notmuch-search-add-tag-thread tag))))
 
 (defun notmuch-search-remove-tag (tag)
-  "Remove a tag from the currently selected thread.
+  "Remove a tag from the currently selected thread or region.
 
-The tag is removed from all messages in the currently selected thread."
+The tag is removed from all messages in the currently selected thread
+or region which match the current search terms."
   (interactive
-   (list (notmuch-select-tag-with-completion "Tag to remove: " (notmuch-search-find-thread-id))))
-  (notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id))
-  (notmuch-search-set-tags (delete tag (notmuch-search-get-tags))))
+   (list (notmuch-select-tag-with-completion
+         "Tag to remove: "
+         (if (region-active-p)
+             (mapconcat 'identity
+                        (notmuch-search-find-thread-id-region (region-beginning) (region-end))
+                        " ")
+           (notmuch-search-find-thread-id)))))
+  (save-excursion
+    (if (region-active-p)
+       (let* ((beg (region-beginning))
+              (end (region-end)))
+         (notmuch-search-remove-tag-region tag beg end))
+      (notmuch-search-remove-tag-thread tag))))
 
 (defun notmuch-search-archive-thread ()
   "Archive the currently selected thread (remove its \"inbox\" tag).
 
 This function advances the next thread when finished."
   (interactive)
-  (notmuch-search-remove-tag "inbox")
+  (notmuch-search-remove-tag-thread "inbox")
   (forward-line))
 
 (defun notmuch-search-process-sentinel (proc msg)