mu4e: support message-action when marking 'deferred'

When marking headers as 'deferred' (with '*'), and executing them ('x'),
you can now also apply arbitrary header actions to them.
This commit is contained in:
djcb
2015-04-26 17:09:06 +03:00
parent 7eb244b3b0
commit 976711c16c
3 changed files with 107 additions and 91 deletions

View File

@ -563,6 +563,7 @@ after the end of the search results."
(mu4e~headers-defun-mark-for untrash) (mu4e~headers-defun-mark-for untrash)
(mu4e~headers-defun-mark-for unmark) (mu4e~headers-defun-mark-for unmark)
(mu4e~headers-defun-mark-for unread) (mu4e~headers-defun-mark-for unread)
(mu4e~headers-defun-mark-for action)
;;; headers-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; headers-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -636,6 +637,7 @@ after the end of the search results."
(define-key map (kbd "?") 'mu4e-headers-mark-for-unread) (define-key map (kbd "?") 'mu4e-headers-mark-for-unread)
(define-key map (kbd "!") 'mu4e-headers-mark-for-read) (define-key map (kbd "!") 'mu4e-headers-mark-for-read)
(define-key map (kbd "A") 'mu4e-headers-mark-for-action)
(define-key map (kbd "u") 'mu4e-headers-mark-for-unmark) (define-key map (kbd "u") 'mu4e-headers-mark-for-unmark)
(define-key map (kbd "+") 'mu4e-headers-mark-for-flag) (define-key map (kbd "+") 'mu4e-headers-mark-for-flag)
@ -1514,13 +1516,15 @@ do nothing."
(interactive "P") (interactive "P")
(mu4e-headers-split-view-grow (- (or n 1)))) (mu4e-headers-split-view-grow (- (or n 1))))
(defun mu4e-headers-action () (defun mu4e-headers-action (&optional actionfunc)
"Ask user what to do with message-at-point, then do it. "Ask user what to do with message-at-point, then do it.
The actions are specified in `mu4e-headers-actions'." The actions are specified in `mu4e-headers-actions'. Optionally,
pass ACTIONFUNC, which is a function that takes a msg-plist
argument."
(interactive) (interactive)
(let ((msg (mu4e-message-at-point)) (let ((msg (mu4e-message-at-point))
(actionfunc (mu4e-read-option "Action: " mu4e-headers-actions))) (afunc (or actionfunc (mu4e-read-option "Action: " mu4e-headers-actions))))
(funcall actionfunc msg))) (funcall afunc msg)))
(defun mu4e-headers-mark-and-next (mark) (defun mu4e-headers-mark-and-next (mark)
"Set mark MARK on the message at point or on all messages in the "Set mark MARK on the message at point or on all messages in the

View File

@ -46,8 +46,8 @@ Value is one of the following symbols:
- `apply' automatically apply the marks before doing anything else - `apply' automatically apply the marks before doing anything else
- `ignore' automatically ignore the marks without asking" - `ignore' automatically ignore the marks without asking"
:type '(choice (const ask :tag "ask user whether to ignore marks") :type '(choice (const ask :tag "ask user whether to ignore marks")
(const apply :tag "apply marks without asking") (const apply :tag "apply marks without asking")
(const ignore :tag "ignore marks without asking")) (const ignore :tag "ignore marks without asking"))
:group 'mu4e-headers) :group 'mu4e-headers)
(defvar mu4e-headers-show-target t (defvar mu4e-headers-show-target t
@ -141,59 +141,67 @@ properties are:
(unless mu4e-marks (unless mu4e-marks
(setq mu4e-marks (setq mu4e-marks
'((refile '((refile
:char "r" :char "r"
:prompt "refile" :prompt "refile"
:dyn-target (lambda (target msg) (mu4e-get-refile-folder msg)) :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg))
:action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(delete (delete
:char "D" :char "D"
:prompt "Delete" :prompt "Delete"
:show-target (lambda (target) "delete") :show-target (lambda (target) "delete")
:action (lambda (docid msg target) (mu4e~proc-remove docid))) :action (lambda (docid msg target) (mu4e~proc-remove docid)))
(flag (flag
:char "+" :char "+"
:prompt "+flag" :prompt "+flag"
:show-target (lambda (target) "flag") :show-target (lambda (target) "flag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+F-u-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "+F-u-N")))
(move (move
:char "m" :char "m"
:prompt "move" :prompt "move"
:ask-target mu4e~mark-get-move-target :ask-target mu4e~mark-get-move-target
:action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(read (read
:char "!" :char "!"
:prompt "!read" :prompt "!read"
:show-target (lambda (target) "read") :show-target (lambda (target) "read")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N")))
(trash (trash
:char "d" :char "d"
:prompt "dtrash" :prompt "dtrash"
:dyn-target (lambda (target msg) (mu4e-get-trash-folder msg)) :dyn-target (lambda (target msg) (mu4e-get-trash-folder msg))
:action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N")))
(unflag (unflag
:char "-" :char "-"
:prompt "-unflag" :prompt "-unflag"
:show-target (lambda (target) "unflag") :show-target (lambda (target) "unflag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N")))
(untrash (untrash
:char "=" :char "="
:prompt "=untrash" :prompt "=untrash"
:show-target (lambda (target) "untrash") :show-target (lambda (target) "untrash")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-T"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "-T")))
(unread (unread
:char "?" :char "?"
:prompt "?unread" :prompt "?unread"
:show-target (lambda (target) "unread") :show-target (lambda (target) "unread")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N"))) :action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N")))
(unmark (unmark
:char " " :char " "
:prompt "unmark" :prompt "unmark"
:action (mu4e-error "No action for unmarking")) :action (mu4e-error "No action for unmarking"))
(something (action
:char "*" :char "a"
:prompt "*something" :prompt "action"
:action (mu4e-error "No action for deferred mark")) :ask-target (lambda () (mu4e-read-option "Action: " mu4e-headers-actions))
:action (lambda (docid msg actionfunc)
(save-excursion
(when (mu4e~headers-goto-docid docid)
(mu4e-headers-action actionfunc)))))
(something
:char "*"
:prompt "*something"
:action (mu4e-error "No action for deferred mark"))
))) )))
@ -218,7 +226,8 @@ The following marks are available, and the corresponding props:
`unflag' n mark this message for unflagging `unflag' n mark this message for unflagging
`untrash' n remove the 'trashed' flag from a message `untrash' n remove the 'trashed' flag from a message
`unmark' n unmark this message `unmark' n unmark this message
`unread' n mark the message as unread" `unread' n mark the message as unread
`action' y mark the message for some action."
(interactive) (interactive)
(let* ((msg (mu4e-message-at-point)) (let* ((msg (mu4e-message-at-point))
(docid (mu4e-message-field msg :docid)) (docid (mu4e-message-field msg :docid))
@ -230,8 +239,8 @@ The following marks are available, and the corresponding props:
(target (mu4e~mark-get-dyn-target mark target)) (target (mu4e~mark-get-dyn-target mark target))
(show-fct (plist-get markdesc :show-target)) (show-fct (plist-get markdesc :show-target))
(shown-target (if show-fct (shown-target (if show-fct
(funcall show-fct target) (funcall show-fct target)
target))) (if target (format "%S" target)))))
(unless docid (mu4e-warn "No message on this line")) (unless docid (mu4e-warn "No message on this line"))
(unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Not in headers-mode")) (unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Not in headers-mode"))
(save-excursion (save-excursion
@ -261,9 +270,9 @@ The following marks are available, and the corresponding props:
(defun mu4e~mark-get-move-target () (defun mu4e~mark-get-move-target ()
"Ask for a move target, and propose to create it if it does not exist." "Ask for a move target, and propose to create it if it does not exist."
(interactive) (interactive)
;; (mu4e-message-at-point) ;; raises error if there is none ;; (mu4e-message-at-point) ;; raises error if there is none
(let* ((target (mu4e-ask-maildir "Move message to: ")) (let* ((target (mu4e-ask-maildir "Move message to: "))
(target (if (string= (substring target 0 1) "/") (target (if (string= (substring target 0 1) "/")
target target
(concat "/" target))) (concat "/" target)))
(fulltarget (concat mu4e-maildir target))) (fulltarget (concat mu4e-maildir target)))
@ -283,7 +292,7 @@ The following marks are available, and the corresponding props:
message at point." message at point."
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target))) (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target)))
(if getter (if getter
(funcall getter target (mu4e-message-at-point)) (funcall getter target (mu4e-message-at-point))
target))) target)))
@ -315,11 +324,13 @@ headers in the region. Optionally, provide TARGET (for moves)."
"Ask user for a mark; return (MARK . TARGET). "Ask user for a mark; return (MARK . TARGET).
If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark
as well." as well."
(let* ((marks (mapcar (lambda (markdescr) (cons (plist-get (cdr markdescr) :prompt) (car markdescr))) mu4e-marks)) (let* ((marks (mapcar (lambda (markdescr)
(marks (cons (plist-get (cdr markdescr) :prompt)
(if allow-something (car markdescr)))
marks mu4e-marks))
(assq-delete-all 'something marks))) (marks
(if allow-something
marks (remove-if (lambda (m) (eq 'something (cdr m))) marks)))
(mark (mu4e-read-option prompt marks)) (mark (mu4e-read-option prompt marks))
(target (mu4e~mark-ask-target mark))) (target (mu4e~mark-ask-target mark)))
(cons mark target))) (cons mark target)))
@ -351,7 +362,6 @@ user which one)."
(mu4e-error "Target dir %s does not exist " fulltarget) (mu4e-error "Target dir %s does not exist " fulltarget)
target))) target)))
(defun mu4e-mark-execute-all (&optional no-confirmation) (defun mu4e-mark-execute-all (&optional no-confirmation)
"Execute the actions for all marked messages in this buffer. "Execute the actions for all marked messages in this buffer.
After the actions have been executed succesfully, the affected After the actions have been executed succesfully, the affected
@ -377,15 +387,15 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
(maphash (maphash
(lambda (docid val) (lambda (docid val)
(let* ((mark (car val)) (target (cdr val)) (let* ((mark (car val)) (target (cdr val))
(markdescr (assq mark mu4e-marks)) (markdescr (assq mark mu4e-marks))
(msg (save-excursion (msg (save-excursion
(mu4e~headers-goto-docid docid) (mu4e~headers-goto-docid docid)
(mu4e-message-at-point)))) (mu4e-message-at-point))))
;; note: whenever you do something with the message, ;; note: whenever you do something with the message,
;; it looses its N (new) flag ;; it looses its N (new) flag
(if markdescr (if markdescr
(funcall (plist-get (cdr markdescr) :action) docid msg target) (funcall (plist-get (cdr markdescr) :action) docid msg target)
(mu4e-error "Unrecognized mark %S" mark)))) (mu4e-error "Unrecognized mark %S" mark))))
mu4e~mark-map)) mu4e~mark-map))
(mu4e-mark-unmark-all) (mu4e-mark-unmark-all)
(message nil))))) (message nil)))))
@ -431,8 +441,8 @@ action', return nil means 'don't do anything'."
'( ("apply marks" . apply) '( ("apply marks" . apply)
("ignore marks?" . ignore))))) ("ignore marks?" . ignore)))))
;; we determined what to do... now do it ;; we determined what to do... now do it
(when (eq what 'apply) (when (eq what 'apply)
(mu4e-mark-execute-all t)))))) (mu4e-mark-execute-all t))))))
(provide 'mu4e-mark) (provide 'mu4e-mark)

View File

@ -2050,7 +2050,7 @@ mu4e-headers-mark-subthread}, respectively
@cartouche @cartouche
@verbatim @verbatim
mark for/as | keybinding | description mark for/as | keybinding | description
--------------+-------------+-------------------------- --------------+-------------+------------------------------
'something' | *, <insert> | mark now, decide later 'something' | *, <insert> | mark now, decide later
delete | D, <delete> | delete delete | D, <delete> | delete
flag | + | mark as 'flagged' ('starred') flag | + | mark as 'flagged' ('starred')
@ -2063,6 +2063,7 @@ mu4e-headers-mark-subthread}, respectively
unmark | u | remove mark at point unmark | u | remove mark at point
unmark all | U | remove all marks unmark all | U | remove all marks
unread | ? | marks as unread unread | ? | marks as unread
action | a | apply some action
@end verbatim @end verbatim
@end cartouche @end cartouche
@ -2073,14 +2074,15 @@ messages, this slows things down significantly@footnote{this uses an
in a buffer}. For this reason, you can disable this by setting in a buffer}. For this reason, you can disable this by setting
@code{mu4e-headers-show-target} to @code{nil}. @code{mu4e-headers-show-target} to @code{nil}.
@t{something} is a special kind of mark; you can use it to mark messages for @t{something} is a special kind of mark; you can use it to mark messages
'something', and then decide later what the 'something' should for 'something', and then decide later what the 'something' should
be@footnote{This kind of 'deferred marking' is similar to the facility in be@footnote{This kind of 'deferred marking' is similar to the facility
@t{midnight commander} (@url{http://www.midnight-commander.org/}) and the in @t{dired}, @t{midnight commander}
like, and uses the same key binding (@key{insert}).} Later, you can set the (@url{http://www.midnight-commander.org/}) and the like, and uses the
actual mark using @kbd{M-x mu4e-mark-resolve-deferred-marks} same key binding (@key{insert}).} Later, you can set the actual mark
(@key{#}). Alternatively, @t{mu4e} will ask you when you try to execute the using @kbd{M-x mu4e-mark-resolve-deferred-marks}
marks (@key{x}). (@key{#}). Alternatively, @t{mu4e} will ask you when you try to execute
the marks (@key{x}).
@node Executing the marks @node Executing the marks
@section Executing the marks @section Executing the marks