1
0
mirror of https://github.com/djcb/mu.git synced 2024-06-25 07:28:02 +02:00

* mu4e: add support for custom matcher functions (WIP)

This commit is contained in:
djcb 2012-06-14 21:54:24 +03:00
parent 0d913c1a1f
commit 2367ab2d67
5 changed files with 107 additions and 17 deletions

View File

@ -87,6 +87,26 @@ are of the form:
* SHORTCUT is a one-character shortcut to call this action
* FUNC is a function which receives a message plist as an argument.")
(defvar mu4e-headers-custom-markers
'(("Older than"
(lambda (msg date) (time-less-p (mu4e-msg-field msg :date) date))
(lambda () (mu4e-get-time-date "Match messages before: ")))
("Newer than"
(lambda (msg date) (time-less-p date (mu4e-msg-field msg :date)))
(lambda () (mu4e-get-time-date "Match messages after: ")))
("Bigger than"
(lambda (msg bytes) (> (mu4e-msg-field msg :size) (* 1024 bytes)))
(lambda () (read-number "Match messages bigger than (Kbytes): "))))
"List of custom markers -- functions to mark message that match
some custom function. Each of the list members has the following format:
(NAME PREDICATE-FUNC PARAM-FUNC)
* NAME is the name of the predicate function, and the first character
is the shortcut (so keep those unique).
* PREDICATE-FUNC is a function that takes to parameters, MSG and (optionally) PARAM,
and should return non-nil when there's a match.
* PARAM-FUNC is function that is evaluated once, and its value is then passed to
PREDICATE-FUNC as PARAM. This is useful for getting user-input.")
(defvar mu4e-headers-sortfield 'date
"Field to sort the headers by. Field must be a symbol, one of:
date, subject, size, prio, from, to.")
@ -401,7 +421,7 @@ after the end of the search results."
(define-key map (kbd "u") 'mu4e~headers-mark-unmark)
(define-key map (kbd "+") 'mu4e~headers-mark-flag)
(define-key map (kbd "-") 'mu4e~headers-mark-unflag)
(define-key map (kbd "&") 'mu4e-headers-mark-custom)
(define-key map "m" 'mu4e-headers-mark-for-move-and-next)
(define-key map (kbd "*") 'mu4e~headers-mark-deferred)
@ -737,6 +757,16 @@ header."
(defvar mu4e~headers-regexp-hist nil
"History list of regexps used.")
(defun mu4e~headers-mark-for-each-if (markpair mark-pred &optional param)
"Mark all headers for with predicate function MARK-PRED return
non-nil with MARKPAIR. MARK-PRED is function that takes two
arguments, MSG (the message at point) and PARAM (a user-specified
parameter). MARKPAIR is a cell (MARK . TARGET)."
(mu4e-headers-for-each
(lambda (msg)
(when (funcall mark-pred msg param)
(mu4e-mark-at-point (car markpair) (cdr markpair))))))
(defun mu4e-headers-mark-pattern ()
"Ask user for a kind of mark (move, delete etc.), a field to
match and a regular expression to match with. Then, mark all
@ -750,8 +780,9 @@ matching messages with that mark."
(pattern (read-string
(mu4e-format "Regexp:")
nil 'mu4e~headers-regexp-hist)))
(mu4e-headers-for-each
(lambda (msg)
(mu4e~headers-mark-for-each-if
markpair
(lambda (msg param)
(let* ((do-mark) (value (mu4e-msg-field msg field)))
(setq do-mark
(if (member field '(:to :from :cc :bcc :reply-to))
@ -759,10 +790,16 @@ matching messages with that mark."
(let ((name (car contact)) (email (cdr contact)))
(or (and name (string-match pattern name))
(and email (string-match pattern email))))) value)
(string-match pattern (or value ""))))
(when do-mark
(mu4e-mark-at-point (car markpair) (cdr markpair))))))))
(string-match pattern (or value "")))))))))
(defun mu4e-headers-mark-custom ()
"Mark messages based on a user-provided predicate function."
(interactive)
(let* ((pred (mu4e-read-option "Match function: "
mu4e-headers-custom-markers))
(param (when (cdr pred) (eval (cdr pred))))
(markpair (mu4e~mark-get-markpair "Mark matched messages with: " t)))
(mu4e~headers-mark-for-each-if markpair (car pred) param)))
(defun mu4e~headers-get-thread-info (msg what)
"Get WHAT (a symbol, either path or thread-id) for MSG."
@ -863,8 +900,6 @@ to get it from; it's a symbol, either 'future or 'past."
(pop mu4e~headers-query-future))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar mu4e~headers-search-hist nil
@ -1083,7 +1118,6 @@ maildir)."
(mu4e-mark-handle-when-leaving)
(mu4e-headers-search (concat "\"maildir:" maildir "\""))))
(defun mu4e-headers-split-view-resize (n)
"In horizontal split-view, increase the number of lines shown by
N; in vertical split-view, increase the number of columns shown by

View File

@ -300,4 +300,5 @@ action', return nil means 'don't do anything'"
(when (eq what 'apply)
(mu4e-mark-execute-all t))))))))
(provide 'mu4e-mark)

View File

@ -31,6 +31,7 @@
(require 'html2text)
(require 'mu4e-vars)
(require 'doc-view)
(require 'org) ;; for org-parse-time-string
(defcustom mu4e-html2text-command nil
"Shell command that converts HTML from stdin into plain text on
@ -104,7 +105,8 @@ User now will be presented with a list:
(optionsstr
(mapconcat
(lambda (option)
(when (consp (cdr option))
;; try to detect old-style options...
(when (or (characterp (cdr option)) (null (cdr option)))
(error (concat "Please use the new format for options/actions; "
"see the manual")))
(let* ((kar (substring (car option) 0 1))
@ -325,8 +327,6 @@ http://cr.yp.to/proto/maildir.html "
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mu4e-display-size (size)
"Get a string representation of SIZE (in bytes)."
(cond
@ -807,5 +807,12 @@ displaying it). Do _not_ bury the current buffer, though."
(delete-window win)))))) nil t)))
(defun mu4e-get-time-date (prompt)
"Determine the emacs time value for the time/date entered by user
after PROMPT. Formats are all that are accepted by
`parse-time-string'."
(let ((timestr (read-string (mu4e-format "%s" prompt))))
(apply 'encode-time (org-parse-time-string timestr))))
(provide 'mu4e-utils)
;;; End of mu4e-utils.el

View File

@ -423,9 +423,10 @@ is nil, and otherwise open it."
(define-key map (kbd "<delete>") 'mu4e-view-mark-for-delete)
(define-key map (kbd "<deletechar>") 'mu4e-mark-for-delete)
(define-key map "D" 'mu4e-view-mark-for-delete)
(define-key map "m" 'mu4e-view-mark-for-move)
(define-key map (kbd "D") 'mu4e-view-mark-for-delete)
(define-key map (kbd "m") 'mu4e-view-mark-for-move)
(define-key map (kbd "&") 'mu4e-view-mark-custom)
(define-key map (kbd "+") 'mu4e-view-mark-flag)
(define-key map (kbd "-") 'mu4e-view-mark-unflag)
@ -926,6 +927,10 @@ attachments) in response to a (mu4e~proc-extract 'temp ... )."
(mu4e-mark-for-move-set)
(mu4e-mark-at-point mark)))))
(defun mu4e-view-mark-custom ()
"Run some custom mark function."
(mu4e~view-in-headers-context
(mu4e-headers-mark-custom)))
(defun mu4e~split-view-p ()
"Return t if we're in split-view, nil otherwise."
@ -983,8 +988,7 @@ user that unmarking only works in the header list."
(mu4e~view-mark-set 'deferred)
(mu4e-view-headers-next))
(defun mu4e-view-marked-execute ()
(defun mu4e-view-marked-execute ()
"Execute the marks."
(interactive)
(mu4e~view-in-headers-context

View File

@ -1235,6 +1235,7 @@ Marking can happen in both the @ref{Headers view} and the @ref{Message view}.
* What to mark for::
* Executing the marks::
* Leaving the headers buffer::
* Custom mark functions::
* Some marking examples::
@end menu
@ -1302,6 +1303,48 @@ When you quit the buffer (for example, but doing a new search) with marks being
present, @t{mu4e} asks you what to do with them, depending on the value of the
variable @code{mu4e-headers-leave-behavior} -- see its documentation.
@node Custom mark functions
@section Custom mark functions
Sometimes, the built-in functions to mark messages may not be sufficient for
your needs. For this, @t{mu4e} offers an easy way to define your own custom
mark functions. You can choose one of the custom marker functions using
@key{&} in @ref{Headers view} and @ref{Message view}.
Custom mark functions should be appended to the list
@code{mu4e-headers-custom-markers}. Each of the elements of this list
('markers') is a list with three (or two) elements:
@itemize
@item The name of the marker - as short string describing this marker. The
first character of this string will also be its shortcut, so these should be
unique.
@item a predicate function taking two arguments @t{msg} and @t{param}- first,
@t{msg}, which is the message
plist (see @ref{The message s-expression}); second is a parameter provided by
the third of the marker elements (next item). The predicate function should
return non-nil if the messages matches.
@item (optionally) a function that is evaluated once, and its result is passed as a
parameter to the predicate function. This is useful to ask for user-input.
@end itemize
So, let's look at an example: suppose we want to match all messages that have
more than @emph{n} recipients. We could do it like this:
@lisp
(add-to-list 'mu4e-headers-custom-markers
'("More than n recipients"
(lambda (msg n) (> (+ (length (mu4e-msg-field msg :to))
(length (mu4e-msg-field msg :cc))) n))
(lambda () (read-number "Match messages with more recipients than: "))) t)
@end lisp
After evaluating this, pressing @key{&} should let you choose the custom
marker function, and ask you for the parameters.
As you can see, it's not very hard to define simple functions to match
messages. There are some more examples in the defaults for
`mu4e-headers-custom-markers'; see @file{mu4e-headers.el}.
@node Some marking examples
@section Some marking examples
@ -1316,6 +1359,7 @@ press @key{% + s hello RET}. Note, the menu system helps you here; all you
need to remember is @key{%} for @code{mu4e-headers-mark-pattern}.
@end itemize
@node Actions
@chapter Actions