* 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:
@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user