unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [WIP PATCH 0/4] Add a mark thread option
@ 2014-04-22 20:11 Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 1/4] emacs: search: tidy notmuch-search-foreach-result Mark Walters
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Mark Walters @ 2014-04-22 20:11 UTC (permalink / raw)
  To: notmuch

Allow users to mark threads in the search buffer and then apply
tagging operations to all marked threads.

This was requested on irc and is also something I have wanted several times.

Patch 1 is a cleanup which simplifies the existing code (imo) and
makes the subsequent patches easier.

Patch 2 is large but mostly just passing an argument all the way
throught the call chain.

Patch 3 and 4 do the actual logic.

At the moment the best way to clear all markings is to refresh the
buffer. Maybe that should change (do we even want refresh to clear
markings?)

On light testing it seems to work.

Best wishes

Mark

Mark Walters (4):
  emacs: search: tidy notmuch-search-foreach-result
  emacs: search: add a marked thread variable and add to relevant
    functions
  emacs: search: split foreach-result into list and region variants
  emacs: search: add key binding for marking a thread

 emacs/notmuch.el |  151 ++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 95 insertions(+), 56 deletions(-)

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [WIP PATCH 1/4] emacs: search: tidy notmuch-search-foreach-result
  2014-04-22 20:11 [WIP PATCH 0/4] Add a mark thread option Mark Walters
@ 2014-04-22 20:11 ` Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 2/4] emacs: search: add a marked thread variable and add to relevant functions Mark Walters
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Walters @ 2014-04-22 20:11 UTC (permalink / raw)
  To: notmuch

notmuch-search-foreach-result is cumbersome as it applies the callback
function with the position of the start of the thread as the
argument. It is more natural to move point to the start of each
relevant thread and not pass any arguments to the callback function.
---
 emacs/notmuch.el |   52 ++++++++++++++++++++++++----------------------------
 1 file changed, 24 insertions(+), 28 deletions(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 6c0bc1b..cb7c006 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -378,27 +378,24 @@ (defun notmuch-search-result-end (&optional pos)
 (defun notmuch-search-foreach-result (beg end function)
   "Invoke FUNCTION for each result between BEG and END.
 
-FUNCTION should take one argument.  It will be applied to the
-character position of the beginning of each result that overlaps
-the region between points BEG and END.  As a special case, if (=
-BEG END), FUNCTION will be applied to the result containing point
-BEG."
-
-  (lexical-let ((pos (notmuch-search-result-beginning beg))
-		;; End must be a marker in case function changes the
-		;; text.
-		(end (copy-marker end))
-		;; Make sure we examine at least one result, even if
-		;; (= beg end).
-		(first t))
-    ;; We have to be careful if the region extends beyond the results.
-    ;; In this case, pos could be null or there could be no result at
-    ;; pos.
-    (while (and pos (or (< pos end) first))
-      (when (notmuch-search-get-result pos)
-	(funcall function pos))
-      (setq pos (notmuch-search-result-end pos)
-	    first nil))))
+FUNCTION should take no arguments.  It will be applied at the
+beginning of each result that overlaps the region between points
+BEG and END.  As a special case, if (= BEG END), FUNCTION will be
+applied to the result containing point BEG."
+
+  (when (notmuch-search-get-result)
+    (save-excursion
+      ;; End must be a marker in case function changes the text.
+      (lexical-let ((end (copy-marker end)))
+	;; We always apply function at least once: even if (= BEG END).
+	(goto-char (notmuch-search-result-beginning beg))
+	(funcall function)
+	(notmuch-search-next-thread)
+	(while (and (notmuch-search-get-result)
+		    (< (notmuch-search-result-beginning) end))
+	  (funcall function)
+	  (notmuch-search-next-thread))))))
+
 ;; Unindent the function argument of notmuch-search-foreach-result so
 ;; the indentation of callers doesn't get out of hand.
 (put 'notmuch-search-foreach-result 'lisp-indent-function 2)
@@ -406,8 +403,8 @@ (defun notmuch-search-foreach-result (beg end function)
 (defun notmuch-search-properties-in-region (property beg end)
   (let (output)
     (notmuch-search-foreach-result beg end
-      (lambda (pos)
-	(push (plist-get (notmuch-search-get-result pos) property) output)))
+      (lambda ()
+	(push (plist-get (notmuch-search-get-result) property) output)))
     output))
 
 (defun notmuch-search-find-thread-id (&optional bare)
@@ -503,8 +500,8 @@ (defun notmuch-search-get-tags (&optional pos)
 (defun notmuch-search-get-tags-region (beg end)
   (let (output)
     (notmuch-search-foreach-result beg end
-      (lambda (pos)
-	(setq output (append output (notmuch-search-get-tags pos)))))
+      (lambda ()
+	(setq output (append output (notmuch-search-get-tags)))))
     output))
 
 (defun notmuch-search-interactive-region ()
@@ -543,10 +540,9 @@ (defun notmuch-search-tag (tag-changes &optional beg end only-matched)
 			beg end only-matched)))
     (notmuch-tag search-string tag-changes)
     (notmuch-search-foreach-result beg end
-      (lambda (pos)
+      (lambda ()
 	(notmuch-search-set-tags
-	 (notmuch-update-tags (notmuch-search-get-tags pos) tag-changes)
-	 pos)))))
+	 (notmuch-update-tags (notmuch-search-get-tags) tag-changes))))))
 
 (defun notmuch-search-add-tag (tag-changes &optional beg end)
   "Change tags for the current thread or region (defaulting to add).
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [WIP PATCH 2/4] emacs: search: add a marked thread variable and add to relevant functions
  2014-04-22 20:11 [WIP PATCH 0/4] Add a mark thread option Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 1/4] emacs: search: tidy notmuch-search-foreach-result Mark Walters
@ 2014-04-22 20:11 ` Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 3/4] emacs: search: split foreach-result into list and region variants Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 4/4] emacs: search: add key binding for marking a thread Mark Walters
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Walters @ 2014-04-22 20:11 UTC (permalink / raw)
  To: notmuch

Add a marked thread variable containing a list of thread ids of marked
threads and make it be passed through all relevant functions. This
ends up being quite large as there are quite a few callers.

Alternatively we could overload beg rather than adding a thread-list
variable. This would make a much smaller patch but might be more
confusing.
---
 emacs/notmuch.el |   66 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 38 insertions(+), 28 deletions(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index cb7c006..7b06458 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -100,6 +100,11 @@ (defcustom notmuch-init-file (locate-user-emacs-file "notmuch-config")
 (defvar notmuch-query-history nil
   "Variable to store minibuffer history for notmuch queries")
 
+(defvar notmuch-search-marked-threads nil
+  "Buffer local list of marked threads")
+(make-variable-buffer-local 'notmuch-search-marked-threads)
+(put 'notmuch-search-marked-threads 'permanent-local t)
+
 (defun notmuch-foreach-mime-part (function mm-handle)
   (cond ((stringp (car mm-handle))
          (dolist (part (cdr mm-handle))
@@ -375,7 +380,7 @@ (defun notmuch-search-result-end (&optional pos)
     (next-single-property-change (or pos (point)) 'notmuch-search-result
 				 nil (point-max))))
 
-(defun notmuch-search-foreach-result (beg end function)
+(defun notmuch-search-foreach-result (beg end thread-list function)
   "Invoke FUNCTION for each result between BEG and END.
 
 FUNCTION should take no arguments.  It will be applied at the
@@ -400,9 +405,9 @@ (defun notmuch-search-foreach-result (beg end function)
 ;; the indentation of callers doesn't get out of hand.
 (put 'notmuch-search-foreach-result 'lisp-indent-function 2)
 
-(defun notmuch-search-properties-in-region (property beg end)
+(defun notmuch-search-properties-in-region (property beg end &optional thread-list)
   (let (output)
-    (notmuch-search-foreach-result beg end
+    (notmuch-search-foreach-result beg end thread-list
       (lambda ()
 	(push (plist-get (notmuch-search-get-result) property) output)))
     output))
@@ -421,13 +426,13 @@ (defun notmuch-search-find-stable-query ()
 matched and unmatched messages in the current thread."
   (plist-get (notmuch-search-get-result) :query))
 
-(defun notmuch-search-find-stable-query-region (beg end &optional only-matched)
+(defun notmuch-search-find-stable-query-region (beg end &optional only-matched thread-list)
   "Return the stable query for the current region.
 
 If ONLY-MATCHED is non-nil, include only matched messages.  If it
 is nil, include both matched and unmatched messages."
   (let ((query-list nil) (all (not only-matched)))
-    (dolist (queries (notmuch-search-properties-in-region :query beg end))
+    (dolist (queries (notmuch-search-properties-in-region :query beg end thread-list))
       (when (first queries)
 	(push (first queries) query-list))
       (when (and all (second queries))
@@ -438,17 +443,17 @@ (defun notmuch-search-find-authors ()
   "Return the authors for the current thread"
   (plist-get (notmuch-search-get-result) :authors))
 
-(defun notmuch-search-find-authors-region (beg end)
+(defun notmuch-search-find-authors-region (beg end &optional thread-list)
   "Return a list of authors for the current region"
-  (notmuch-search-properties-in-region :authors beg end))
+  (notmuch-search-properties-in-region :authors beg end thread-list))
 
 (defun notmuch-search-find-subject ()
   "Return the subject for the current thread"
   (plist-get (notmuch-search-get-result) :subject))
 
-(defun notmuch-search-find-subject-region (beg end)
+(defun notmuch-search-find-subject-region (beg end &optional thread-list)
   "Return a list of authors for the current region"
-  (notmuch-search-properties-in-region :subject beg end))
+  (notmuch-search-properties-in-region :subject beg end thread-list))
 
 (defun notmuch-search-show-thread (&optional elide-toggle)
   "Display the currently selected thread."
@@ -497,9 +502,9 @@ (defun notmuch-search-set-tags (tags &optional pos)
 (defun notmuch-search-get-tags (&optional pos)
   (plist-get (notmuch-search-get-result pos) :tags))
 
-(defun notmuch-search-get-tags-region (beg end)
+(defun notmuch-search-get-tags-region (beg end &optional thread-list)
   (let (output)
-    (notmuch-search-foreach-result beg end
+    (notmuch-search-foreach-result beg end thread-list
       (lambda ()
 	(setq output (append output (notmuch-search-get-tags)))))
     output))
@@ -507,24 +512,29 @@ (defun notmuch-search-get-tags-region (beg end)
 (defun notmuch-search-interactive-region ()
   "Return the bounds of the current interactive region.
 
-This returns (BEG END), where BEG and END are the bounds of the
-region if the region is active, or both `point' otherwise."
+This returns (BEG END THREAD-LIST), where BEG and END are the
+bounds of the region if the region is active, or both `point'
+otherwise and THREAD-LIST is a list of the marked threads (if
+any)."
   (if (region-active-p)
-      (list (region-beginning) (region-end))
-    (list (point) (point))))
+      (list (region-beginning) (region-end) notmuch-search-marked-threads)
+    (list (point) (point) notmuch-search-marked-threads)))
 
 (defun notmuch-search-interactive-tag-changes (&optional initial-input)
-  "Prompt for tag changes for the current thread or region.
+  "Prompt for tag changes for the current thread, marked threads or region.
 
-Returns (TAG-CHANGES REGION-BEGIN REGION-END)."
+Returns (TAG-CHANGES REGION-BEGIN REGION-END THREAD-LIST)."
   (let* ((region (notmuch-search-interactive-region))
 	 (beg (first region)) (end (second region))
-	 (prompt (if (= beg end) "Tag thread" "Tag region")))
+	 (thread-list (third region))
+	 (prompt (if thread-list
+		     "Tag marked threads"
+		   (if (= beg end) "Tag thread" "Tag region"))))
     (cons (notmuch-read-tag-changes
-	   (notmuch-search-get-tags-region beg end) prompt initial-input)
+	   (notmuch-search-get-tags-region beg end thread-list) prompt initial-input)
 	  region)))
 
-(defun notmuch-search-tag (tag-changes &optional beg end only-matched)
+(defun notmuch-search-tag (tag-changes &optional beg end only-matched thread-list)
   "Change tags for the currently selected thread or region.
 
 See `notmuch-tag' for information on the format of TAG-CHANGES.
@@ -537,30 +547,30 @@ (defun notmuch-search-tag (tag-changes &optional beg end only-matched)
   (interactive (notmuch-search-interactive-tag-changes))
   (unless (and beg end) (setq beg (point) end (point)))
   (let ((search-string (notmuch-search-find-stable-query-region
-			beg end only-matched)))
+			beg end only-matched thread-list)))
     (notmuch-tag search-string tag-changes)
-    (notmuch-search-foreach-result beg end
+    (notmuch-search-foreach-result beg end thread-list
       (lambda ()
 	(notmuch-search-set-tags
 	 (notmuch-update-tags (notmuch-search-get-tags) tag-changes))))))
 
-(defun notmuch-search-add-tag (tag-changes &optional beg end)
+(defun notmuch-search-add-tag (tag-changes &optional beg end thread-list)
   "Change tags for the current thread or region (defaulting to add).
 
 Same as `notmuch-search-tag' but sets initial input to '+'."
   (interactive (notmuch-search-interactive-tag-changes "+"))
-  (notmuch-search-tag tag-changes beg end))
+  (notmuch-search-tag tag-changes beg end nil thread-list))
 
-(defun notmuch-search-remove-tag (tag-changes &optional beg end)
+(defun notmuch-search-remove-tag (tag-changes &optional beg end thread-list)
   "Change tags for the current thread or region (defaulting to remove).
 
 Same as `notmuch-search-tag' but sets initial input to '-'."
   (interactive (notmuch-search-interactive-tag-changes "-"))
-  (notmuch-search-tag tag-changes beg end))
+  (notmuch-search-tag tag-changes beg end nil thread-list))
 
 (put 'notmuch-search-archive-thread 'notmuch-prefix-doc
      "Un-archive the currently selected thread.")
-(defun notmuch-search-archive-thread (&optional unarchive beg end)
+(defun notmuch-search-archive-thread (&optional unarchive beg end thread-list)
   "Archive the currently selected thread or region.
 
 Archive each message in the currently selected thread by applying
@@ -573,7 +583,7 @@ (defun notmuch-search-archive-thread (&optional unarchive beg end)
   (interactive (cons current-prefix-arg (notmuch-search-interactive-region)))
   (when notmuch-archive-tags
     (notmuch-search-tag
-     (notmuch-tag-change-list notmuch-archive-tags unarchive) beg end))
+     (notmuch-tag-change-list notmuch-archive-tags unarchive) beg end thread-list))
   (notmuch-search-next-thread))
 
 (defun notmuch-search-update-result (result &optional pos)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [WIP PATCH 3/4] emacs: search: split foreach-result into list and region variants
  2014-04-22 20:11 [WIP PATCH 0/4] Add a mark thread option Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 1/4] emacs: search: tidy notmuch-search-foreach-result Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 2/4] emacs: search: add a marked thread variable and add to relevant functions Mark Walters
@ 2014-04-22 20:11 ` Mark Walters
  2014-04-22 20:11 ` [WIP PATCH 4/4] emacs: search: add key binding for marking a thread Mark Walters
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Walters @ 2014-04-22 20:11 UTC (permalink / raw)
  To: notmuch

---
 emacs/notmuch.el |   20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 7b06458..e1ece47 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -380,7 +380,20 @@ (defun notmuch-search-result-end (&optional pos)
     (next-single-property-change (or pos (point)) 'notmuch-search-result
 				 nil (point-max))))
 
-(defun notmuch-search-foreach-result (beg end thread-list function)
+(defun notmuch-search-foreach-result-in-list (thread-list function)
+  "Invoke function for each thread in list."
+  (lexical-let ((thread-list (copy-sequence thread-list)))
+    (save-excursion
+      (goto-char (point-min))
+      (while (and (notmuch-search-get-result)
+		  thread-list)
+	(let ((thread (notmuch-search-find-thread-id)))
+	  (when (member thread thread-list)
+	    (funcall function)
+	    (setq thread-list (delete thread thread-list))))
+	(notmuch-search-next-thread)))))
+
+(defun notmuch-search-foreach-result-in-region (beg end function)
   "Invoke FUNCTION for each result between BEG and END.
 
 FUNCTION should take no arguments.  It will be applied at the
@@ -401,6 +414,11 @@ (defun notmuch-search-foreach-result (beg end thread-list function)
 	  (funcall function)
 	  (notmuch-search-next-thread))))))
 
+(defun notmuch-search-foreach-result (beg end thread-list function)
+  (if thread-list
+      (notmuch-search-foreach-result-in-list thread-list function)
+    (notmuch-search-foreach-result-in-region beg end function)))
+
 ;; Unindent the function argument of notmuch-search-foreach-result so
 ;; the indentation of callers doesn't get out of hand.
 (put 'notmuch-search-foreach-result 'lisp-indent-function 2)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [WIP PATCH 4/4] emacs: search: add key binding for marking a thread
  2014-04-22 20:11 [WIP PATCH 0/4] Add a mark thread option Mark Walters
                   ` (2 preceding siblings ...)
  2014-04-22 20:11 ` [WIP PATCH 3/4] emacs: search: split foreach-result into list and region variants Mark Walters
@ 2014-04-22 20:11 ` Mark Walters
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Walters @ 2014-04-22 20:11 UTC (permalink / raw)
  To: notmuch

Add a function for toggling the mark on a thread, by default bound to
",".  Also make a marked thread clearly visible: this uses inverse
video as being a bit like selecting. Perhaps actually using the same
face (mainly backgorund colour) as a selection would make sense?
---
 emacs/notmuch.el |   15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index e1ece47..4976f0a 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -179,6 +179,7 @@ (defvar notmuch-search-mode-map
     (define-key map "+" 'notmuch-search-add-tag)
     (define-key map (kbd "RET") 'notmuch-search-show-thread)
     (define-key map "Z" 'notmuch-tree-from-search-current-query)
+    (define-key map "," 'notmuch-search-mark-thread)
     map)
   "Keymap for \"notmuch search\" buffers.")
 (fset 'notmuch-search-mode-map notmuch-search-mode-map)
@@ -604,6 +605,17 @@ (defun notmuch-search-archive-thread (&optional unarchive beg end thread-list)
      (notmuch-tag-change-list notmuch-archive-tags unarchive) beg end thread-list))
   (notmuch-search-next-thread))
 
+(defun notmuch-search-mark-thread ()
+  "Toggle the mark on the currently selected thread."
+  (interactive)
+  (let ((thread (notmuch-search-find-thread-id)))
+    (if (member thread notmuch-search-marked-threads)
+	(setq notmuch-search-marked-threads
+	      (delete thread notmuch-search-marked-threads))
+      (push (notmuch-search-find-thread-id) notmuch-search-marked-threads)))
+  (notmuch-search-update-result (notmuch-search-get-result))
+  (notmuch-search-next-thread))
+
 (defun notmuch-search-update-result (result &optional pos)
   "Replace the result object of the thread at POS (or point) by
 RESULT and redraw it.
@@ -806,6 +818,9 @@ (defun notmuch-search-show-result (result pos)
       (dolist (spec notmuch-search-result-format)
 	(notmuch-search-insert-field (car spec) (cdr spec) result))
       (insert "\n")
+      (when (member (concat "thread:" (plist-get result :thread))
+		    notmuch-search-marked-threads)
+	(notmuch-apply-face nil '(:inverse-video t) nil pos (point)))
       (notmuch-search-color-line pos (point) (plist-get result :tags))
       (put-text-property pos (point) 'notmuch-search-result result))))
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-04-22 20:12 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-22 20:11 [WIP PATCH 0/4] Add a mark thread option Mark Walters
2014-04-22 20:11 ` [WIP PATCH 1/4] emacs: search: tidy notmuch-search-foreach-result Mark Walters
2014-04-22 20:11 ` [WIP PATCH 2/4] emacs: search: add a marked thread variable and add to relevant functions Mark Walters
2014-04-22 20:11 ` [WIP PATCH 3/4] emacs: search: split foreach-result into list and region variants Mark Walters
2014-04-22 20:11 ` [WIP PATCH 4/4] emacs: search: add key binding for marking a thread Mark Walters

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).