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 unmark)
(mu4e~headers-defun-mark-for unread)
(mu4e~headers-defun-mark-for action)
;;; 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-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 "+") 'mu4e-headers-mark-for-flag)
@ -1514,13 +1516,15 @@ do nothing."
(interactive "P")
(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.
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)
(let ((msg (mu4e-message-at-point))
(actionfunc (mu4e-read-option "Action: " mu4e-headers-actions)))
(funcall actionfunc msg)))
(afunc (or actionfunc (mu4e-read-option "Action: " mu4e-headers-actions))))
(funcall afunc msg)))
(defun mu4e-headers-mark-and-next (mark)
"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
- `ignore' automatically ignore the marks without asking"
:type '(choice (const ask :tag "ask user whether to ignore marks")
(const apply :tag "apply marks without asking")
(const ignore :tag "ignore marks without asking"))
(const apply :tag "apply marks without asking")
(const ignore :tag "ignore marks without asking"))
:group 'mu4e-headers)
(defvar mu4e-headers-show-target t
@ -97,7 +97,7 @@ where
(with-current-buffer b
(eq major-mode 'mu4e-headers-mode)))
(buffer-list)))
(defmacro mu4e~mark-in-context (&rest body)
"Evaluate BODY in the context of the headers buffer in case this
is either a headers or view buffer."
@ -141,59 +141,67 @@ properties are:
(unless mu4e-marks
(setq mu4e-marks
'((refile
:char "r"
:prompt "refile"
: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")))
(delete
:char "D"
:prompt "Delete"
:show-target (lambda (target) "delete")
:action (lambda (docid msg target) (mu4e~proc-remove docid)))
(flag
:char "+"
:prompt "+flag"
:show-target (lambda (target) "flag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+F-u-N")))
(move
:char "m"
:prompt "move"
:ask-target mu4e~mark-get-move-target
:action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(read
:char "!"
:prompt "!read"
:show-target (lambda (target) "read")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N")))
(trash
:char "d"
:prompt "dtrash"
: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")))
(unflag
:char "-"
:prompt "-unflag"
:show-target (lambda (target) "unflag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N")))
(untrash
:char "="
:prompt "=untrash"
:show-target (lambda (target) "untrash")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-T")))
(unread
:char "?"
:prompt "?unread"
:show-target (lambda (target) "unread")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N")))
(unmark
:char " "
:prompt "unmark"
:action (mu4e-error "No action for unmarking"))
(something
:char "*"
:prompt "*something"
:action (mu4e-error "No action for deferred mark"))
'((refile
:char "r"
:prompt "refile"
: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")))
(delete
:char "D"
:prompt "Delete"
:show-target (lambda (target) "delete")
:action (lambda (docid msg target) (mu4e~proc-remove docid)))
(flag
:char "+"
:prompt "+flag"
:show-target (lambda (target) "flag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+F-u-N")))
(move
:char "m"
:prompt "move"
:ask-target mu4e~mark-get-move-target
:action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(read
:char "!"
:prompt "!read"
:show-target (lambda (target) "read")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N")))
(trash
:char "d"
:prompt "dtrash"
: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")))
(unflag
:char "-"
:prompt "-unflag"
:show-target (lambda (target) "unflag")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N")))
(untrash
:char "="
:prompt "=untrash"
:show-target (lambda (target) "untrash")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-T")))
(unread
:char "?"
:prompt "?unread"
:show-target (lambda (target) "unread")
:action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N")))
(unmark
:char " "
:prompt "unmark"
:action (mu4e-error "No action for unmarking"))
(action
:char "a"
:prompt "action"
: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
`untrash' n remove the 'trashed' flag from a 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)
(let* ((msg (mu4e-message-at-point))
(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))
(show-fct (plist-get markdesc :show-target))
(shown-target (if show-fct
(funcall show-fct target)
target)))
(funcall show-fct target)
(if target (format "%S" target)))))
(unless docid (mu4e-warn "No message on this line"))
(unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Not in headers-mode"))
(save-excursion
@ -261,9 +270,9 @@ The following marks are available, and the corresponding props:
(defun mu4e~mark-get-move-target ()
"Ask for a move target, and propose to create it if it does not exist."
(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: "))
(target (if (string= (substring target 0 1) "/")
(target (if (string= (substring target 0 1) "/")
target
(concat "/" target)))
(fulltarget (concat mu4e-maildir target)))
@ -283,7 +292,7 @@ The following marks are available, and the corresponding props:
message at point."
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target)))
(if getter
(funcall getter target (mu4e-message-at-point))
(funcall getter target (mu4e-message-at-point))
target)))
@ -315,11 +324,13 @@ headers in the region. Optionally, provide TARGET (for moves)."
"Ask user for a mark; return (MARK . TARGET).
If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark
as well."
(let* ((marks (mapcar (lambda (markdescr) (cons (plist-get (cdr markdescr) :prompt) (car markdescr))) mu4e-marks))
(marks
(if allow-something
marks
(assq-delete-all 'something marks)))
(let* ((marks (mapcar (lambda (markdescr)
(cons (plist-get (cdr markdescr) :prompt)
(car markdescr)))
mu4e-marks))
(marks
(if allow-something
marks (remove-if (lambda (m) (eq 'something (cdr m))) marks)))
(mark (mu4e-read-option prompt marks))
(target (mu4e~mark-ask-target mark)))
(cons mark target)))
@ -351,7 +362,6 @@ user which one)."
(mu4e-error "Target dir %s does not exist " fulltarget)
target)))
(defun mu4e-mark-execute-all (&optional no-confirmation)
"Execute the actions for all marked messages in this buffer.
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
(lambda (docid val)
(let* ((mark (car val)) (target (cdr val))
(markdescr (assq mark mu4e-marks))
(msg (save-excursion
(mu4e~headers-goto-docid docid)
(mu4e-message-at-point))))
(markdescr (assq mark mu4e-marks))
(msg (save-excursion
(mu4e~headers-goto-docid docid)
(mu4e-message-at-point))))
;; note: whenever you do something with the message,
;; it looses its N (new) flag
(if markdescr
(funcall (plist-get (cdr markdescr) :action) docid msg target)
(mu4e-error "Unrecognized mark %S" mark))))
(funcall (plist-get (cdr markdescr) :action) docid msg target)
(mu4e-error "Unrecognized mark %S" mark))))
mu4e~mark-map))
(mu4e-mark-unmark-all)
(message nil)))))
@ -413,7 +423,7 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
(defun mu4e-mark-marks-num ()
"Return the number of marks in the current buffer."
(if mu4e~mark-map (hash-table-count mu4e~mark-map) 0))
(defun mu4e-mark-handle-when-leaving ()
"If there are any marks in the current buffer, handle those
@ -431,9 +441,9 @@ action', return nil means 'don't do anything'."
'( ("apply marks" . apply)
("ignore marks?" . ignore)))))
;; we determined what to do... now do it
(when (eq what 'apply)
(mu4e-mark-execute-all t))))))
(when (eq what 'apply)
(mu4e-mark-execute-all t))))))
(provide 'mu4e-mark)
;; End of mu4e-mark.el

View File

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