* new feature: deferred marking, i.e. mark now, decide what for later (WIP)

This commit is contained in:
djcb 2012-06-14 19:10:02 +03:00
parent 6c61b44a87
commit 0d913c1a1f
5 changed files with 167 additions and 83 deletions

8
TODO
View File

@ -18,13 +18,14 @@
- contact completion (see Jacek's 'mu4e: using' mail)
- actions for /all/ headers, actions for /all/ attachment
- custom header fields in headers-view, message-view
- custom predicate functions for marking
- guile integration
- check if we can speed up mu4e-proc parsing by using search rather than
regexp search
- show maildirs as a tree, not a list in speed bar
- mark message, decide what to do with them later
- make killing all windows (i.e.. 'fullscreen mode' optional)
- improve fringe marks (see https://github.com/djcb/mu/issues/21)
- better naming for draft buffers
- review emacs menus
** Done
@ -50,7 +51,8 @@
https://github.com/djcb/mu/issues/26)
- *FIX* don't remove unknown message flags when moving
- make guile/gtk/webkit dependency optional
- improve fringe marks (see https://github.com/djcb/mu/issues/21)
- mark message, decide what to do with them later (i.e.. 'deferred marking')
# Local Variables:

View File

@ -30,7 +30,9 @@
(eval-when-compile (byte-compile-disable-warning 'cl-functions))
(require 'cl)
(require 'fringe)
(require 'hl-line)
(require 'mu4e-utils) ;; utility functions
(require 'mu4e-proc)
(require 'mu4e-vars)
@ -310,24 +312,34 @@ after the end of the search results."
;; highlight the first message
(mu4e~headers-highlight (mu4e~headers-docid-at-point (point-min)))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro mu4e~headers-defun-mark-func (mark)
"Define a function mu4e~headers-mark-MARK."
(let ((funcname (intern (concat "mu4e~headers-mark-" (symbol-name mark))))
(docstring (concat "Mark header at point with " (symbol-name mark) ".")))
`(defun ,funcname () ,docstring
(interactive)
(mu4e-headers-mark-and-next (quote ,mark)))))
;; define our mark functions; there must be some way to do this in a loop but
;; since `mu4e~headers-defun-mark-func' is a macro, the argument must be a
;; literal value.
(mu4e~headers-defun-mark-func trash)
(mu4e~headers-defun-mark-func delete)
(mu4e~headers-defun-mark-func read)
(mu4e~headers-defun-mark-func unread)
(mu4e~headers-defun-mark-func flag)
(mu4e~headers-defun-mark-func unflag)
(mu4e~headers-defun-mark-func deferred)
(mu4e~headers-defun-mark-func unmark)
;;; headers-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar mu4e-headers-mode-map nil
"Keymap for *mu4e-headers* buffers.")
(unless mu4e-headers-mode-map
;; add some quick funcs so our key descriptions below are shorter
;; TODO: defmacro this
(defun mu4e~headers-mark-trash()(interactive)(mu4e-headers-mark-and-next 'trash))
(defun mu4e~headers-mark-delete()(interactive)(mu4e-headers-mark-and-next 'delete))
(defun mu4e~headers-mark-unmark()(interactive)(mu4e-headers-mark-and-next 'unmark))
(defun mu4e~headers-mark-read()(interactive)(mu4e-headers-mark-and-next 'read))
(defun mu4e~headers-mark-unread()(interactive)(mu4e-headers-mark-and-next 'unread))
(defun mu4e~headers-mark-flag()(interactive)(mu4e-headers-mark-and-next 'flag))
(defun mu4e~headers-mark-unflag()(interactive)(mu4e-headers-mark-and-next 'unflag))
(setq mu4e-headers-mode-map
(let ((map (make-sparse-keymap)))
@ -391,7 +403,11 @@ after the end of the search results."
(define-key map (kbd "-") 'mu4e~headers-mark-unflag)
(define-key map "m" 'mu4e-headers-mark-for-move-and-next)
(define-key map (kbd "*") 'mu4e~headers-mark-deferred)
(define-key map (kbd "<kp-multiply>") 'mu4e~headers-mark-deferred)
(define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks)
(define-key map "U" 'mu4e-mark-unmark-all)
(define-key map "x" 'mu4e-mark-execute-all)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -718,25 +734,7 @@ header."
(when msg
(funcall func msg))))))
(defun mu4e~headers-get-markpair ()
"Ask user for a mark; return (MARK . TARGET)."
(let* ((mark
(mu4e-read-option "Mark to set: "
'( ("move" . move)
("dtrash" . trash)
("Delete" . delete)
("ounread" . unread)
("read" . read)
("+flag" . flag)
("-unflag" . unflag)
("unmark" . unmark))))
(target
(when (eq mark 'move)
(mu4e-ask-maildir-check-exists "Move message to: "))))
(cons mark target)))
(defvar mu4e~headers-regexp-hist nil
(defvar mu4e~headers-regexp-hist nil
"History list of regexps used.")
(defun mu4e-headers-mark-pattern ()
@ -744,7 +742,7 @@ header."
match and a regular expression to match with. Then, mark all
matching messages with that mark."
(interactive)
(let ((markpair (mu4e~headers-get-markpair))
(let ((markpair (mu4e~mark-get-markpair "Mark matched messages with: " t))
(field (mu4e-read-option "Field to match: "
'( ("subject" . :subject)
("from" . :from)
@ -789,7 +787,10 @@ limited to the message at point and its descendants."
(mu4e-message-at-point t) 'thread-id))
(path (mu4e~headers-get-thread-info
(mu4e-message-at-point t) 'path))
(markpair (mu4e~headers-get-markpair))
(markpair
(mu4e~mark-get-markpair
(if subthread "Mark subthread with: " "Mark whole thread with: ")
t))
(last-marked-point))
(mu4e-headers-for-each
(lambda (msg)

View File

@ -52,8 +52,8 @@ particularly fast).")
"Map (hash) of docid->markinfo; when a message is marked, the
information is added here.
markinfo is a list consisting of the following:
\(mark target)
markinfo is a cons cell consisting of the following:
\(mark . target)
where
MARK is the type of mark (move, trash, delete)
TARGET (optional) is the target directory (for 'move')")
@ -99,6 +99,7 @@ The following marks are available, and the corresponding props:
`unread' n mark the message as unread
`flag' n mark this message for flagging
`unflag' n mark this message for unflagging
`deferred' n mark this message for *something* (decided later)
`unmark' n unmark this message"
(interactive)
(let* ((docid (mu4e~headers-docid-at-point))
@ -106,16 +107,17 @@ The following marks are available, and the corresponding props:
;; target (the target folder) the other ones get a pseudo "target", as
;; info for the user.
(markcell
(case mark ;; the visual mark
('move `("m" . ,target))
('trash '("d" . "trash"))
('delete '("D" . "delete"))
('unread '("o" . "unread"))
('read '("r" . "read"))
('flag '("+" . "flag"))
('unflag '("-" . "unflag"))
('unmark '(" " . nil))
(t (error "Invalid mark %S" mark))))
(case mark
(move `("m" . ,target))
(trash '("d" . "trash"))
(delete '("D" . "delete"))
(unread '("o" . "unread"))
(read '("r" . "read"))
(flag '("+" . "flag"))
(unflag '("-" . "unflag"))
(deferred '("*" . "deferred"))
(unmark '(" " . nil))
(otherwise (error "Invalid mark %S" mark))))
(markkar (car markcell))
(target (cdr markcell)))
(unless docid (error "No message on this line"))
@ -128,7 +130,7 @@ The following marks are available, and the corresponding props:
(remove-overlays (line-beginning-position) (line-end-position))
;; now, let's set a mark (unless we were unmarking)
(unless (eql mark 'unmark)
(puthash docid (list mark target) mu4e~mark-map)
(puthash docid (cons mark target) mu4e~mark-map)
;; when we have a target (ie., when moving), show the target folder in
;; an overlay
(when (and target mu4e-headers-show-target)
@ -178,6 +180,45 @@ provided, function asks for it."
(mu4e-mark-set 'move target))))
(defun mu4e~mark-get-markpair (prompt &optional allow-deferred)
"Ask user for a mark; return (MARK . TARGET). If ALLOW-DEFERRED
is non-nil, allow the 'deferred' pseudo mark as well."
(let* ((marks '(("move" . move)
("dtrash" . trash)
("Delete" . delete)
("ounread" . unread)
("read" . read)
("+flag" . flag)
("-unflag" . unflag)
("unmark" . unmark)))
(marks
(if allow-deferred
(append marks (list '("*deferred" . deferred)))
marks))
(mark (mu4e-read-option prompt marks))
(target
(when (eq mark 'move)
(mu4e-ask-maildir-check-exists "Move message to: "))))
(cons mark target)))
(defun mu4e-mark-resolve-deferred-marks ()
"Check if there are any deferred marks. If there are such marks,
replace them with a _real_ mark (ask the user which one)."
(interactive)
(let ((markpair))
(maphash
(lambda (docid val)
(let ((mark (car val)) (target (cdr val)))
(when (eql mark 'deferred)
(unless markpair
(setq markpair
(mu4e~mark-get-markpair "Set deferred mark to: " nil)))
(save-excursion
(when (mu4e~headers-goto-docid docid)
(mu4e-mark-set (car markpair) (cdr markpair)))))))
mu4e~mark-map)))
(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
@ -194,13 +235,14 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
(let ((marknum (hash-table-count mu4e~mark-map)))
(if (zerop marknum)
(message "Nothing is marked")
(mu4e-mark-resolve-deferred-marks)
(when (or no-confirmation
(y-or-n-p
(format "Are you sure you want to execute %d mark%s?"
marknum (if (> marknum 1) "s" ""))))
(maphash
(lambda (docid val)
(let ((mark (nth 0 val)) (target (nth 1 val)))
(let ((mark (car val)) (target (cdr val)))
(case mark
(move (mu4e~proc-move docid target))
(read (mu4e~proc-move docid nil "+S-u-N"))
@ -211,7 +253,8 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
(unless mu4e-trash-folder
(error "`mu4e-trash-folder' not set"))
(mu4e~proc-move docid mu4e-trash-folder "+T"))
(delete (mu4e~proc-remove docid)))))
(delete (mu4e~proc-remove docid))
(otherwise (error "Unrecognized mark %S" mark)))))
mu4e~mark-map))
(mu4e-mark-unmark-all)
(message nil))))

View File

@ -428,7 +428,11 @@ is nil, and otherwise open it."
(define-key map (kbd "+") 'mu4e-view-mark-flag)
(define-key map (kbd "-") 'mu4e-view-mark-unflag)
(define-key map (kbd "*") 'mu4e-view-mark-deferred)
(define-key map (kbd "<kp-multiply>") 'mu4e-view-mark-deferred)
(define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks)
;; misc
(define-key map "w" 'mu4e-view-toggle-wrap-lines)
(define-key map "h" 'mu4e-view-toggle-hide-cited)
@ -973,6 +977,13 @@ user that unmarking only works in the header list."
(mu4e~view-mark-set 'unflag)
(mu4e-view-headers-next))
(defun mu4e-view-mark-deferred ()
"Mark the current message for unflagging."
(interactive)
(mu4e~view-mark-set 'deferred)
(mu4e-view-headers-next))
(defun mu4e-view-marked-execute ()
"Execute the marks."
(interactive)

View File

@ -561,6 +561,9 @@ U unmark *all* messages
% mark based on a regular expression
T,t mark whole thread, subthread
SPC deferred mark (decide what to mark for later)
* resolve deferred marks
x execute actions for the marked messages
composition
@ -646,7 +649,6 @@ include the previously captured message as an attachment, using
The file @file{mu4e-actions.el} in the @t{mu4e} source distribution contains a
number of example actions.
@subsection Split view
@ -738,10 +740,12 @@ or by using the keyboard; the default bindings are:
@verbatim
key description
--- -----------
==============================================================
n,p go to next, previous message
y select the headers view (if it's visible)
searching
---------
s search
e edit last query
/ narrow the search
@ -752,23 +756,32 @@ j jump to maildir
M-left previous query
M-right next query
C-+,C-- increase / decrease the number of headers shown
a execute some action on the message
marking
-------
d mark for moving to the trash folder
DEL,D mark for immediate deletion
m mark for moving to another maildir folder
+,- mark for flagging/unflagging
o,r mark message as unread, read
u unmark message at point
U unmark *all* messages
% mark based on a regular expression
T,t mark whole thread, subthread
SPC deferred mark (decide what to mark for later)
* resolve deferred marks
x execute actions for the marked messages
composition
-----------
R,F,C reply/forward/compose
E edit (only allowed for draft messages)
. show the raw message view. 'q' takes you back.
actions
-------
g go to (visit) numbered URL (using `browse-url')
(or: <mouse-2> or RET with point on url)
e extract (save) attachment (asks for number)
@ -776,10 +789,17 @@ e extract (save) attachment (asks for number)
C-u e will extract multiple attachments
o open attachment (asks for number)
(or: <S-mouse-2> or S-RET with point on attachment)
A execute some action on an attachment
a execute some custom action on the message
A execute some custom action on an attachment
misc
----
w toggle line wrapping
h toggle showing cited parts
. show the raw message view. 'q' takes you back.
C-+,C-- increase / decrease the number of headers shown
H get help
q,z leave the message view
@end verbatim
@ -1251,18 +1271,23 @@ apply to messages:
| unflag | - | remove 'flagged' mark |
| read | r | mark as read |
| unread | o | marks as unread |
| deferred | * | mark now, decide later |
| unmark | u | remove mark at point |
| remove all | U | remove all marks |
| unmark all | U | remove all marks |
@end verbatim
After marking a header for something, the leftmost columns shows a character
(same as the keybinding) to remind you what you marked it with. Next to that,
@t{mu4e} displays the name of the mark, on top of the beginning of the header
line. This latter display is informative, but if you often mark many
(thousands) messages, this may slow down things significantly@footnote{this
uses an emacs feature called @emph{overlays}, which are slow when used a lot
in a buffer}. For this reason, you can disable this by setting
@code{mu4e-headers-show-target} to @code{nil}.
After marking a header for something, the left-most columns shows a character
to remind you what you marked it with. Next to that, @t{mu4e} displays the
name of the mark, on top of the beginning of the header line. This latter
display is informative, but if you often mark many (thousands) messages, this
may slow down things significantly@footnote{this uses an emacs feature called
@emph{overlays}, which are slow when used a lot in a buffer}. For this reason,
you can disable this by setting @code{mu4e-headers-show-target} to @code{nil}.
@t{deferred} is a special kind of mark; you can use it to mark some messages,
and then decide later what mark to use for them. At any time, you can set the
actual mark with @code{mu4e-mark-resolve-deferred-marks} (@key{#}), or
@t{mu4e} will ask you for it when you execute the marks (@key{x}).
@node Executing the marks
@section Executing the marks
@ -1304,12 +1329,14 @@ You can invoke the actions with @key{a} for actions on messages, and @key{A}
for actions on attachments. In the following, we'll gives some examples of
defining actions.
Note, the format of the actions has changed slightly since version 0.9.8.4;
@t{mu4e} warns you if you use the old format still. The older format was:
@code{(DESCRIPTION SHORTCUT [VALUE])}, while the new format is a cons-cell,
@code{(DESCRIPTION . VALUE)}; see below for some examples. If your shortcut is
not also the first character of the description, simply prefix the description
with that character.
Note, the format of the actions has changed since version 0.9.8.4, and you
must change your configuration to use the new format; @t{mu4e} warns you when
you are using the old format.
The older format was: @code{(DESCRIPTION SHORTCUT [VALUE])}, while the new
format is a cons-cell, @code{(DESCRIPTION . VALUE)}; see below for some
examples. If your shortcut is not also the first character of the description,
simply prefix the description with that character.
@subsection Functions for actions
@ -1334,7 +1361,7 @@ After you have defined your function, you can add it to the list of actions,
either @code{mu4e-headers-actions}, @code{mu4e-view-actions} or
@code{mu4e-view-attachment-actions}.
Let's now look at some simple examples.
Let's take a at some simple examples.
@subsection Example: adding an action in the headers view
@ -1398,9 +1425,9 @@ To help a bit with this, all functions and variables in @t{mu4e} marked for
letters, so they will only appear at the end of completion buffers and the
like.
Functions that start with @t{mu4e-view-} and @t{mu4e-headers-} should be called
only from that particular context (the message view and the headers view,
respectively).
Functions that start with @t{mu4e-view-} and @t{mu4e-headers-} should be
called only from that particular context (the message view and the headers
view, respectively).
@subsection Example actions
@ -1918,7 +1945,7 @@ can also use functions like @code{mu4e-headers-mark-thread} (@key{T}),
time, and @code{mu4e-headers-mark-pattern} (@key{%}) to mark all messages
matching a certain regular expression.
@item @emph{How can I use @t{BBDB}?} Currently, there is no built-in for
address management with @t{BBDB}; instead, we recommend @xref{Maintaining an
address management with @t{BBDB}; instead, we recommend @ref{Maintaining an
address-book with org-contacts} for now.
@item @emph{mu4e seems to return a mere subset of all matches - how can I get
all?}. Indeed, for speed reasons (and because, if you are like the author, you