* mu4e-hdrs: add marking for threads & subthreads, and more:

- fix find headers docid (this was b0rked)
  - don't use mu4e-choose-action anymore, use mu4e-read-option
  - some typo fixes
This commit is contained in:
djcb
2012-04-26 22:42:15 +03:00
parent 2f3bd58c03
commit 9220d6095c

View File

@ -41,7 +41,6 @@
"Settings for the headers view." "Settings for the headers view."
:group 'mu4e) :group 'mu4e)
(defcustom mu4e-headers-fields (defcustom mu4e-headers-fields
'( (:date . 25) '( (:date . 25)
(:flags . 6) (:flags . 6)
@ -98,17 +97,16 @@ are of the form:
;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst mu4e~hdrs-fringe " " "*internal* The space on the left of (defconst mu4e~hdrs-fringe " " "*internal* The space on the left of
message headers to put marks.") message headers to put marks.")
(defconst mu4e-docid-sepa "\004" (defconst mu4e~docid-pre "\376"
"Each header starts (invisibly) with the docid followd by `mu4e-docid-sepa'.") "Each header starts (invisibly) with the `mu4e-docid-pre',
followed by the docid, followd by `mu4e-docid-post'.")
(defconst mu4e~docid-post "\377"
"Each header starts (invisibly) with the `mu4e-docid-pre',
followed by the docid, followd by `mu4e-docid-post'.")
(defun mu4e~hdrs-clear () (defun mu4e~hdrs-clear ()
"Clear the header buffer and related data structures." "Clear the header buffer and related data structures."
@ -126,6 +124,7 @@ results, otherwise, limit number of results to
`mu4e-search-results-limit'." `mu4e-search-results-limit'."
(let ((buf (get-buffer-create mu4e~hdrs-buffer-name)) (let ((buf (get-buffer-create mu4e~hdrs-buffer-name))
(inhibit-read-only t)) (inhibit-read-only t))
(mu4e-mark-handle-when-leaving)
(with-current-buffer buf (with-current-buffer buf
(mu4e-hdrs-mode) (mu4e-hdrs-mode)
(setq (setq
@ -249,6 +248,7 @@ if provided, or at the end of the buffer otherwise."
(:subject (:subject
(concat ;; prefix subject with a thread indicator (concat ;; prefix subject with a thread indicator
(mu4e-thread-prefix (plist-get msg :thread)) (mu4e-thread-prefix (plist-get msg :thread))
;; "["(plist-get (plist-get msg :thread) :path) "] "
val)) val))
((:maildir :path) val) ((:maildir :path) val)
((:to :from :cc :bcc) (mu4e~hdrs-contact-str val)) ((:to :from :cc :bcc) (mu4e~hdrs-contact-str val))
@ -334,6 +334,9 @@ after the end of the search results."
(define-key map "g" 'mu4e-rerun-search) ;; for compatibility (define-key map "g" 'mu4e-rerun-search) ;; for compatibility
(define-key map "%" 'mu4e-hdrs-mark-matches) (define-key map "%" 'mu4e-hdrs-mark-matches)
(define-key map "t" 'mu4e-hdrs-mark-subthread)
(define-key map "T" 'mu4e-hdrs-mark-thread)
;; navigation ;; navigation
(define-key map "n" 'mu4e-next-header) (define-key map "n" 'mu4e-next-header)
@ -501,9 +504,11 @@ adding a lot of new headers looks really choppy."
(defun mu4e~docid-cookie (docid) (defun mu4e~docid-cookie (docid)
"Create an invisible string containing DOCID; this is to be used "Create an invisible string containing DOCID; this is to be used
at the beginning of lines to identify headers." at the beginning of lines to identify headers."
(propertize (format "%d%s" docid mu4e-docid-sepa) (propertize (format "%s%d%s"
mu4e~docid-pre docid mu4e~docid-post)
'docid docid 'invisible t)) 'docid docid 'invisible t))
(defun mu4e~docid-at-point (&optional point) (defun mu4e~docid-at-point (&optional point)
"Get the docid for the header at POINT, or at current (point) if "Get the docid for the header at POINT, or at current (point) if
nil. Returns the docid, or nil if there is none." nil. Returns the docid, or nil if there is none."
@ -520,8 +525,8 @@ of the beginning of the line."
(let ((oldpoint (point)) (newpoint)) (let ((oldpoint (point)) (newpoint))
(goto-char (point-min)) (goto-char (point-min))
(setq newpoint (setq newpoint
(search-forward (mu4e~docid-cookie docid) nil t)) (search-forward (mu4e~docid-cookie docid) nil t))
(when (null to-mark) (unless to-mark
(if (null newpoint) (if (null newpoint)
(goto-char oldpoint) ;; not found; restore old pos (goto-char oldpoint) ;; not found; restore old pos
(progn (progn
@ -553,17 +558,17 @@ with DOCID which must be present in the headers buffer."
(error "Cannot find message with docid %S" docid)) (error "Cannot find message with docid %S" docid))
;; now, we're at the beginning of the header, looking at ;; now, we're at the beginning of the header, looking at
;; <docid>\004 ;; <docid>\004
;; (which is invisible). jumpp past that… ;; (which is invisible). jump past that…
(unless (re-search-forward mu4e-docid-sepa nil t) (unless (re-search-forward mu4e~docid-post nil t)
(error "Cannot find the `mu4e-docid-sepa' separator")) (error "Cannot find the `mu4e~docid-post' separator"))
;; we found the separatpr we move point one to the right for the ;; we found the separator we move point one to the right for the
;; the area to write the marker. ;; the area to write the marker.
;;(forward-char) ;;(forward-char)
;; clear old marks, and add the new ones. ;; clear old marks, and add the new ones.
(delete-char (length mu4e~hdrs-fringe)) (delete-char (length mu4e~hdrs-fringe))
(insert (propertize mark 'face 'mu4e-hdrs-marks-face) " ") (insert (propertize mark 'face 'mu4e-hdrs-marks-face) " ") ;; FIXME
(goto-char oldpoint)))) (goto-char oldpoint))))
(defun mu4e~hdrs-add-header (str docid point &optional msg) (defun mu4e~hdrs-add-header (str docid point &optional msg)
"Add header STR with DOCID to the buffer at POINT if non-nil, or "Add header STR with DOCID to the buffer at POINT if non-nil, or
@ -601,46 +606,40 @@ non-nill, don't raise an error when the docid is not found."
s-expression for the corresponding header." s-expression for the corresponding header."
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(while (search-forward mu4e-docid-sepa nil t) (while (search-forward mu4e~docid-pre nil t)
(let ((msg (get-text-property (point) 'msg))) ;; not really sure why we need to jump to bol; we we need
;; to, otherwise we miss lines sometimes...
(let ((msg (get-text-property (line-beginning-position) 'msg)))
(when msg (when msg
(funcall func msg)))))) (funcall func msg))))))
(defun mu4e~hdrs-get-markpair ()
"Ask user for a mark; return (MARK . TARGET)."
(let* ((mark
(mu4e-read-option "Mark to set: "
'( ("move" nil move)
("trash" ?d trash)
("elete" ?D delete)
("unread" ?o unread)
("read" nil read)
("unmark" nil unmark))))
(target
(when (eq mark 'move)
(mu4e-ask-maildir-check-exists "Move message to: "))))
(cons mark target)))
(defun mu4e-hdrs-mark-matches () (defun mu4e-hdrs-mark-matches ()
"Ask user for a kind of mark (move, delete etc.), a field to "Ask user for a kind of mark (move, delete etc.), a field to
match and a regular expression to match with. Then, mark all match and a regular expression to match with. Then, mark all
matching messages with that mark." matching messages with that mark."
(interactive) (interactive)
(let* ((target) (mark) (let ((markpair (mu4e~hdrs-get-markpair))
(markkar (field (mu4e-read-option "Field to match: "
(mu4e-read-option "Mark to set: " '(("subject" nil :subject)
'( ("move" ?m) ("from" nil :from)
("trash" ?d) ("to" nil :to))))
("delete" ?D) (pattern (read-string "Regexp: ")))
("unread" ?o)
("read" ?r)
("unmark" ?u))))
(mark
(case markkar
(?m
(setq target (mu4e-ask-maildir-check-exists "Move message to: "))
'move)
(?d 'trash)
(?D 'delete)
(?o 'unread)
(?r 'read)
(?u 'unmark)))
(fieldkar
(mu4e-read-option "Field to match: "
'(("subject" ?s)
("from" ?f)
("to" ?t))))
(field
(case fieldkar
(?s :subject)
(?f :from)
(?t :to)))
(pattern (read-string "Regexp: ")))
(mu4e-hdrs-for-each (mu4e-hdrs-for-each
(lambda (msg) (lambda (msg)
(let* ((do-mark) (value (mu4e-msg-field msg field))) (let* ((do-mark) (value (mu4e-msg-field msg field)))
@ -652,7 +651,54 @@ matching messages with that mark."
(and email (string-match pattern email))))) value) (and email (string-match pattern email))))) value)
(string-match pattern (or value "")))) (string-match pattern (or value ""))))
(when do-mark (when do-mark
(mu4e-mark-at-point mark target))))))) (mu4e-mark-at-point (car markpair) (cdr markpair))))))))
(defun mu4e~hdrs-get-thread-info (msg what)
"Get WHAT (a symbol, either path or thread-id) for MSG."
(let* ((thread (or (plist-get msg :thread) (error "No thread info found")))
(path (or (plist-get thread :path) (error "No threadpath found"))))
(case what
(path path)
(thread-id
(save-match-data
;; the thread id is the first segment of the thread path
(when (string-match "^\\([[:xdigit:]]+\\):?" path)
(match-string 1 path))))
(otherwise (error "Not supported")))))
(defun mu4e-hdrs-mark-thread (&optional subthread)
"Mark the thread at point, if SUBTHREAD is non-nil, marking is
limited to the message at point and its descendants."
;; the tread id is shared by all messages in a thread
(interactive "P")
(let* ((thread-id (mu4e~hdrs-get-thread-info
(mu4e-message-at-point t) 'thread-id))
(path (mu4e~hdrs-get-thread-info
(mu4e-message-at-point t) 'path))
(markpair (mu4e~hdrs-get-markpair)))
(mu4e-hdrs-for-each
(lambda (msg)
(let ((my-thread-id (mu4e~hdrs-get-thread-info msg 'thread-id)))
(if subthread
;; subthread matching; msg's thread path should have path as its
;; prefix
(when (string-match (concat "^" path)
(mu4e~hdrs-get-thread-info msg 'path))
(mu4e-mark-at-point (car markpair) (cdr markpair)))
;; nope; not looking for the subthread; looking for the whole thread
(when (string= thread-id
(mu4e~hdrs-get-thread-info msg 'thread-id))
(mu4e-mark-at-point (car markpair) (cdr markpair)))))))))
(defun mu4e-hdrs-mark-subthread ()
"Like `mu4e-mark-thread', but only for a sub-thread."
(interactive)
(mu4e-hdrs-mark-thread t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -663,16 +709,15 @@ matching messages with that mark."
results to `mu4e-search-results-limit', otherwise show all. In results to `mu4e-search-results-limit', otherwise show all. In
other words, use the C-u prefix to get /all/ results, otherwise get other words, use the C-u prefix to get /all/ results, otherwise get
up to `mu4e-search-results-limit' much quicker." up to `mu4e-search-results-limit' much quicker."
(interactive "s[mu] search for: ") (interactive "s[mu] search for: ")
(when (mu4e-mark-handle-when-leaving) (mu4e-hdrs-search expr current-prefix-arg))
(mu4e-hdrs-search expr current-prefix-arg)))
(defun mu4e-search-bookmark () (defun mu4e-search-bookmark ()
"Search using some bookmarked query. With C-u prefix, show /all/ results, "Search using some bookmarked query. With C-u prefix, show /all/ results,
otherwise, limit to up to `mu4e-search-results-limit'." otherwise, limit to up to `mu4e-search-results-limit'."
(interactive) (interactive)
(let ((query (mu4e-ask-bookmark "Bookmark: "))) (let ((query (mu4e-ask-bookmark "Bookmark: ")))
(when (and query (mu4e-mark-handle-when-leaving)) (when query
(mu4e-hdrs-search query current-prefix-arg)))) (mu4e-hdrs-search query current-prefix-arg))))
(defun mu4e-search-bookmark-edit-first (expr) (defun mu4e-search-bookmark-edit-first (expr)
@ -682,7 +727,7 @@ otherwise, limit to up to `mu4e-search-results-limit'."
(interactive (interactive
(list (read-string "[mu] search for: " (list (read-string "[mu] search for: "
(concat (or (mu4e-ask-bookmark "Edit bookmark: ") "") " ")))) (concat (or (mu4e-ask-bookmark "Edit bookmark: ") "") " "))))
(when (and expr (mu4e-mark-handle-when-leaving)) (when expr
(mu4e-hdrs-search expr current-prefix-arg))) (mu4e-hdrs-search expr current-prefix-arg)))
@ -726,23 +771,22 @@ current window. "
(defun mu4e~hdrs-kill-buffer-and-window () (defun mu4e~hdrs-kill-buffer-and-window ()
"Quit the message view and return to the main view." "Quit the message view and return to the main view."
(interactive) (interactive)
(when (mu4e-mark-handle-when-leaving) (mu4e-mark-handle-when-leaving)
(let ((buf mu4e~hdrs-buffer)) (let ((buf mu4e~hdrs-buffer))
(when (buffer-live-p buf) (when (buffer-live-p buf)
(bury-buffer) (bury-buffer)
(delete-windows-on buf) ;; destroy all windows for this buffer (delete-windows-on buf) ;; destroy all windows for this buffer
(kill-buffer buf))) (kill-buffer buf)))
(mu4e~main-view))) (mu4e~main-view))
(defun mu4e-rerun-search () (defun mu4e-rerun-search ()
"Rerun the search for the last search expression; if none exists, "Rerun the search for the last search expression; if none exists,
do a new search." do a new search."
(interactive) (interactive)
(when (mu4e-mark-handle-when-leaving)
(if mu4e-last-expr (if mu4e-last-expr
(mu4e-hdrs-search mu4e-last-expr) (mu4e-hdrs-search mu4e-last-expr)
(call-interactively 'mu4e-search)))) (call-interactively 'mu4e-search)))
(defun mu4e~hdrs-move (lines) (defun mu4e~hdrs-move (lines)
"Move point LINES lines forward (if LINES is positive) or "Move point LINES lines forward (if LINES is positive) or
@ -784,7 +828,8 @@ maildir). With C-u prefix, show /all/ results, otherwise, limit to
up to `mu4e-search-results-limit'." up to `mu4e-search-results-limit'."
(interactive) (interactive)
(let ((fld (mu4e-ask-maildir "Jump to maildir: "))) (let ((fld (mu4e-ask-maildir "Jump to maildir: ")))
(when (and fld (mu4e-mark-handle-when-leaving)) (when fld
(mu4e-mark-handle-when-leaving)
(mu4e-hdrs-search (concat "\"maildir:" fld "\"") (mu4e-hdrs-search (concat "\"maildir:" fld "\"")
current-prefix-arg)))) current-prefix-arg))))
@ -846,7 +891,7 @@ for draft messages."
actions are specified in `mu4e-headers-actions'." actions are specified in `mu4e-headers-actions'."
(interactive) (interactive)
(let ((msg (mu4e-message-at-point t)) (let ((msg (mu4e-message-at-point t))
(actionfunc (mu4e-choose-action "Action: " mu4e-headers-actions))) (actionfunc (mu4e-read-option "Action: " mu4e-headers-actions)))
(funcall actionfunc msg))) (funcall actionfunc msg)))
(defun mu4e-hdrs-mark-and-next (mark) (defun mu4e-hdrs-mark-and-next (mark)