* 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
1 changed files with 111 additions and 66 deletions

View File

@ -41,7 +41,6 @@
"Settings for the headers view."
:group 'mu4e)
(defcustom mu4e-headers-fields
'( (:date . 25)
(:flags . 6)
@ -98,17 +97,16 @@ are of the form:
;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst mu4e~hdrs-fringe " " "*internal* The space on the left of
message headers to put marks.")
(defconst mu4e-docid-sepa "\004"
"Each header starts (invisibly) with the docid followd by `mu4e-docid-sepa'.")
(defconst mu4e~docid-pre "\376"
"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 ()
"Clear the header buffer and related data structures."
@ -126,6 +124,7 @@ results, otherwise, limit number of results to
`mu4e-search-results-limit'."
(let ((buf (get-buffer-create mu4e~hdrs-buffer-name))
(inhibit-read-only t))
(mu4e-mark-handle-when-leaving)
(with-current-buffer buf
(mu4e-hdrs-mode)
(setq
@ -249,6 +248,7 @@ if provided, or at the end of the buffer otherwise."
(:subject
(concat ;; prefix subject with a thread indicator
(mu4e-thread-prefix (plist-get msg :thread))
;; "["(plist-get (plist-get msg :thread) :path) "] "
val))
((:maildir :path) 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 "%" 'mu4e-hdrs-mark-matches)
(define-key map "t" 'mu4e-hdrs-mark-subthread)
(define-key map "T" 'mu4e-hdrs-mark-thread)
;; navigation
(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)
"Create an invisible string containing DOCID; this is to be used
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))
(defun mu4e~docid-at-point (&optional point)
"Get the docid for the header at POINT, or at current (point) if
nil. Returns the docid, or nil if there is none."
@ -520,8 +525,8 @@ of the beginning of the line."
(let ((oldpoint (point)) (newpoint))
(goto-char (point-min))
(setq newpoint
(search-forward (mu4e~docid-cookie docid) nil t))
(when (null to-mark)
(search-forward (mu4e~docid-cookie docid) nil t))
(unless to-mark
(if (null newpoint)
(goto-char oldpoint) ;; not found; restore old pos
(progn
@ -553,17 +558,17 @@ with DOCID which must be present in the headers buffer."
(error "Cannot find message with docid %S" docid))
;; now, we're at the beginning of the header, looking at
;; <docid>\004
;; (which is invisible). jumpp past that…
(unless (re-search-forward mu4e-docid-sepa nil t)
(error "Cannot find the `mu4e-docid-sepa' separator"))
;; we found the separatpr we move point one to the right for the
;; (which is invisible). jump past that…
(unless (re-search-forward mu4e~docid-post nil t)
(error "Cannot find the `mu4e~docid-post' separator"))
;; we found the separator we move point one to the right for the
;; the area to write the marker.
;;(forward-char)
;; clear old marks, and add the new ones.
(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))))
(defun mu4e~hdrs-add-header (str docid point &optional msg)
"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."
(save-excursion
(goto-char (point-min))
(while (search-forward mu4e-docid-sepa nil t)
(let ((msg (get-text-property (point) 'msg)))
(while (search-forward mu4e~docid-pre nil t)
;; 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
(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 ()
"Ask user for a kind of mark (move, delete etc.), a field to
match and a regular expression to match with. Then, mark all
matching messages with that mark."
(interactive)
(let* ((target) (mark)
(markkar
(mu4e-read-option "Mark to set: "
'( ("move" ?m)
("trash" ?d)
("delete" ?D)
("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: ")))
(let ((markpair (mu4e~hdrs-get-markpair))
(field (mu4e-read-option "Field to match: "
'(("subject" nil :subject)
("from" nil :from)
("to" nil :to))))
(pattern (read-string "Regexp: ")))
(mu4e-hdrs-for-each
(lambda (msg)
(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)
(string-match pattern (or value ""))))
(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
other words, use the C-u prefix to get /all/ results, otherwise get
up to `mu4e-search-results-limit' much quicker."
(interactive "s[mu] search for: ")
(when (mu4e-mark-handle-when-leaving)
(mu4e-hdrs-search expr current-prefix-arg)))
(interactive "s[mu] search for: ")
(mu4e-hdrs-search expr current-prefix-arg))
(defun mu4e-search-bookmark ()
"Search using some bookmarked query. With C-u prefix, show /all/ results,
otherwise, limit to up to `mu4e-search-results-limit'."
(interactive)
(let ((query (mu4e-ask-bookmark "Bookmark: ")))
(when (and query (mu4e-mark-handle-when-leaving))
(when query
(mu4e-hdrs-search query current-prefix-arg))))
(defun mu4e-search-bookmark-edit-first (expr)
@ -682,7 +727,7 @@ otherwise, limit to up to `mu4e-search-results-limit'."
(interactive
(list (read-string "[mu] search for: "
(concat (or (mu4e-ask-bookmark "Edit bookmark: ") "") " "))))
(when (and expr (mu4e-mark-handle-when-leaving))
(when expr
(mu4e-hdrs-search expr current-prefix-arg)))
@ -726,23 +771,22 @@ current window. "
(defun mu4e~hdrs-kill-buffer-and-window ()
"Quit the message view and return to the main view."
(interactive)
(when (mu4e-mark-handle-when-leaving)
(let ((buf mu4e~hdrs-buffer))
(when (buffer-live-p buf)
(bury-buffer)
(delete-windows-on buf) ;; destroy all windows for this buffer
(kill-buffer buf)))
(mu4e~main-view)))
(mu4e-mark-handle-when-leaving)
(let ((buf mu4e~hdrs-buffer))
(when (buffer-live-p buf)
(bury-buffer)
(delete-windows-on buf) ;; destroy all windows for this buffer
(kill-buffer buf)))
(mu4e~main-view))
(defun mu4e-rerun-search ()
"Rerun the search for the last search expression; if none exists,
do a new search."
(interactive)
(when (mu4e-mark-handle-when-leaving)
(if mu4e-last-expr
(mu4e-hdrs-search mu4e-last-expr)
(call-interactively 'mu4e-search))))
(call-interactively 'mu4e-search)))
(defun mu4e~hdrs-move (lines)
"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'."
(interactive)
(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 "\"")
current-prefix-arg))))
@ -846,7 +891,7 @@ for draft messages."
actions are specified in `mu4e-headers-actions'."
(interactive)
(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)))
(defun mu4e-hdrs-mark-and-next (mark)