mu4e: marks: clarify the way targets are obtained.

:get-target is split into:
- :ask-target (run once per bulk operation)
- :dyn-target (run once per message)

A side benefit is that the existence of the target of a move is not
checked for every message when doing bulk moves.
This commit is contained in:
Jean-Philippe Bernardy 2014-11-27 11:10:51 +01:00
parent 3981f5412f
commit 818eedf59f
1 changed files with 35 additions and 27 deletions

View File

@ -127,22 +127,24 @@ properties are:
:char The character to display in the headers view :char The character to display in the headers view
:prompt The prompt to use when asking for marks (used for :prompt The prompt to use when asking for marks (used for
example when marking a whole thread) example when marking a whole thread)
:get-target The function to obtain the target for the mark :ask-target Get the target. This function run once per
action, or nil if the action requires no target. bulk-operation, and thus is suitable for user-interaction.
If nil, the target is nil.
:dyn-target Compute the dynamic target. This is run once per
message, which is passed as an argument. If nil, the target
is not touched.
:show-target How to display the target. :show-target How to display the target.
:action The action to apply on the message. :action The action to apply on the message.
") ")
;; TODO: the arguments taken by the various functions should probably ;; TODO: The actions should probably get the
;; be different. 1. It appears that the TARGET argument of
;; :get-target is always nil. 2. The actions should probably get the
;; message: it contains more info than the docid. ;; message: it contains more info than the docid.
(unless mu4e-marks (unless mu4e-marks
(setq mu4e-marks (setq mu4e-marks
`((refile '((refile
:char "r" :char "r"
:prompt "refile" :prompt "refile"
:get-target (lambda (target msg) (mu4e-get-refile-folder msg)) :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg))
:show-target (lambda (target) target) :show-target (lambda (target) target)
:action (lambda (docid target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) :action (lambda (docid target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(delete (delete
@ -158,7 +160,7 @@ properties are:
(move (move
:char "m" :char "m"
:prompt "move" :prompt "move"
:get-target (lambda (target msg) (mu4e~mark-get-move-target target)) :ask-target mu4e~mark-get-move-target
:show-target (lambda (target) target) :show-target (lambda (target) target)
:action (lambda (docid target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) :action (lambda (docid target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")))
(read (read
@ -169,7 +171,7 @@ properties are:
(trash (trash
:char "d" :char "d"
:prompt "dtrash" :prompt "dtrash"
:get-target (lambda (target msg) (mu4e-get-trash-folder msg)) :dyn-target (lambda (target msg) (mu4e-get-trash-folder msg))
:show-target (lambda (target) target) :show-target (lambda (target) target)
:action (lambda (docid target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N"))) :action (lambda (docid target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N")))
(unflag (unflag
@ -200,10 +202,10 @@ properties are:
))) )))
(defun mu4e-mark-at-point (mark &optional target) (defun mu4e-mark-at-point (mark target)
"Mark (or unmark) message at point. "Mark (or unmark) message at point.
MARK specifies the mark-type. For `move'-marks and `trash'-marks MARK specifies the mark-type. For `move'-marks and `trash'-marks
there is also the TARGET argument, which specifies to which the TARGET argument is non-nil and specifies to which
maildir the message is to be moved/trashed. The function works in maildir the message is to be moved/trashed. The function works in
both headers buffers and message buffers. both headers buffers and message buffers.
@ -230,7 +232,8 @@ The following marks are available, and the corresponding props:
;; info for the user. ;; info for the user.
(markdesc (cdr (or (assq mark mu4e-marks) (mu4e-error "Invalid mark %S" mark)))) (markdesc (cdr (or (assq mark mu4e-marks) (mu4e-error "Invalid mark %S" mark))))
(markkar (plist-get markdesc :char)) (markkar (plist-get markdesc :char))
(target (funcall (plist-get markdesc :show-target) target))) (target (mu4e~mark-get-dyn-target mark target))
(shown-target (funcall (plist-get markdesc :show-target) 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
@ -246,7 +249,7 @@ The following marks are available, and the corresponding props:
;; when we have a target (ie., when moving), show the target folder in ;; when we have a target (ie., when moving), show the target folder in
;; an overlay ;; an overlay
(when (and target mu4e-headers-show-target) (when (and target mu4e-headers-show-target)
(let* ((targetstr (propertize (concat "-> " target " ") (let* ((targetstr (propertize (concat "-> " shown-target " ")
'face 'mu4e-system-face)) 'face 'mu4e-system-face))
;; mu4e~headers-goto-docid docid t \will take us just after the ;; mu4e~headers-goto-docid docid t \will take us just after the
;; docid cookie and then we skip the mu4e~mark-fringe ;; docid cookie and then we skip the mu4e~mark-fringe
@ -257,34 +260,40 @@ The following marks are available, and the corresponding props:
docid))))))) docid)))))))
(defun mu4e~mark-get-move-target (&optional target) (defun mu4e~mark-get-move-target ()
"Ask for a move target, and propose to create it if it does not exist. If TARGET is provided, "Ask for a move target, and propose to create it if it does not exist."
this function merely checks the validity of the move."
(interactive) (interactive)
;; (mu4e-message-at-point) ;; raises error if there is none ;; (mu4e-message-at-point) ;; raises error if there is none
(let* ((target (or 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)))
(when (or (file-directory-p fulltarget) (when (or (file-directory-p fulltarget)
(and (yes-or-no-p (and (yes-or-no-p
(format "%s does not exist. Create now?" fulltarget)) (format "%s does not exist. Create now?" fulltarget))
(mu4e~proc-mkdir fulltarget))) (mu4e~proc-mkdir fulltarget)))
target))) target)))
(defun mu4e~mark-get-target (mark &optional target) (defun mu4e~mark-ask-target (mark)
"Get the target for MARK, if it is a mark that has a target; "Ask the target for MARK, if the user should be asked the target."
otherwise return nil." (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :ask-target)))
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :get-target))) (and getter (funcall getter))))
(and getter (funcall getter target (mu4e-message-at-point)))))
(defun mu4e~mark-get-dyn-target (mark target)
"Get the dynamic target for MARK. The result may depend on the
message at point."
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target)))
(if getter
(funcall getter target (mu4e-message-at-point))
target)))
(defun mu4e-mark-set (mark &optional target) (defun mu4e-mark-set (mark &optional target)
"Mark the header at point, or, if region is active, mark all "Mark the header at point, or, if region is active, mark all
headers in the region. Optionally, provide TARGET (for moves)." headers in the region. Optionally, provide TARGET (for moves)."
(unless target (unless target
(setq target (mu4e~mark-get-target mark target))) (setq target (mu4e~mark-ask-target mark)))
(if (not (use-region-p)) (if (not (use-region-p))
;; single message ;; single message
(mu4e-mark-at-point mark target) (mu4e-mark-at-point mark target)
@ -293,7 +302,6 @@ headers in the region. Optionally, provide TARGET (for moves)."
(let ((cant-go-further) (eor (region-end))) (let ((cant-go-further) (eor (region-end)))
(goto-char (region-beginning)) (goto-char (region-beginning))
(while (and (<= (point) eor) (not cant-go-further)) (while (and (<= (point) eor) (not cant-go-further))
(setq target (mu4e~mark-get-target mark target))
(mu4e-mark-at-point mark target) (mu4e-mark-at-point mark target)
(setq cant-go-further (not (mu4e-headers-next)))))))) (setq cant-go-further (not (mu4e-headers-next))))))))
@ -315,7 +323,7 @@ as well."
marks marks
(assq-delete-all 'something marks))) (assq-delete-all 'something marks)))
(mark (mu4e-read-option prompt marks)) (mark (mu4e-read-option prompt marks))
(target (mu4e~mark-get-target mark))) (target (mu4e~mark-ask-target mark)))
(cons mark target))) (cons mark target)))