Allow applying filters to summary consecutively

* lisp/mail/rmailsum.el (rmail-summary-currently-displayed-msgs):
New variable.
(rmail-summary-fill-displayed-messages, rmail-summary-negate): New
functions.
(rmail-summary-by-labels, rmail-summary-by-recipients)
(rmail-summary-by-regexp, rmail-summary-by-topic)
(rmail-summary-by-senders): Accept an additional argument
KEEP-FILTERING, to narrow the existing filtered summary.

* etc/NEWS: Announce the change.
This commit is contained in:
Andrea Monaco 2022-10-27 16:56:12 +03:00 committed by Eli Zaretskii
parent 877f706c86
commit c8b9ba5fa1
2 changed files with 122 additions and 24 deletions

View file

@ -2051,6 +2051,18 @@ Formerly it was a pair of numbers '(A B)' that represented 65536*A + B,
to cater to older Emacs implementations that lacked bignums. to cater to older Emacs implementations that lacked bignums.
The older form still works but is undocumented. The older form still works but is undocumented.
** Rmail
---
*** Rmail partial summaries can now be applied one on top of the other.
You can now narrow the filtering of messages by the summary's criteria
(recipients, topic, senders, etc.) by making a summary of the already
summarized messages. For example, invoking 'rmail-summary-by-senders',
followed by 'rmail-summary-by-topic' will produce a summary where both
the senders and the topic are according to your selection. Most Rmail
summary commands can be told to filter the existing summary by
invoking them with the prefix argument.
** EIEIO ** EIEIO
+++ +++

View file

@ -50,6 +50,13 @@ Setting this option to nil might speed up the generation of summaries."
:type 'boolean :type 'boolean
:group 'rmail-summary) :group 'rmail-summary)
(defvar rmail-summary-currently-displayed-msgs nil
"String made of `y' and `n'.
At position i it tells wether message i is shown on the summary or not.
First character is ignored. Used when applying rmail-summary-by-* commands
consecutively.")
(put 'rmail-summary-currently-displayed-msgs 'permanent-local t)
(defvar rmail-summary-font-lock-keywords (defvar rmail-summary-font-lock-keywords
'(("^ *[0-9]+D.*" . font-lock-string-face) ; Deleted. '(("^ *[0-9]+D.*" . font-lock-string-face) ; Deleted.
("^ *[0-9]+-.*" . font-lock-type-face) ; Unread. ("^ *[0-9]+-.*" . font-lock-type-face) ; Unread.
@ -267,6 +274,34 @@ Setting this option to nil might speed up the generation of summaries."
(defun rmail-update-summary (&rest _) (defun rmail-update-summary (&rest _)
(apply (car rmail-summary-redo) (cdr rmail-summary-redo))) (apply (car rmail-summary-redo) (cdr rmail-summary-redo)))
(defun rmail-summary-fill-displayed-messages ()
"Fill the `rmail-summary-currently-displayed-msgs' string."
(with-current-buffer rmail-buffer
(with-current-buffer rmail-summary-buffer
(setq rmail-summary-currently-displayed-msgs
(make-string (1+ rmail-total-messages) ?n))
(goto-char (point-min))
(while (not (eobp))
(aset rmail-summary-currently-displayed-msgs
(string-to-number (thing-at-point 'line))
?y)
(forward-line 1)))))
(defun rmail-summary-negate ()
"Toggle showing messages that match/don't match the current summary."
(interactive)
(rmail-summary-fill-displayed-messages)
(rmail-new-summary "Negate"
'(rmail-summary-by-regexp ".*" t)
(lambda (msg)
(if
(= (aref rmail-summary-currently-displayed-msgs msg)
?n)
(progn
(aset rmail-summary-currently-displayed-msgs msg ?y) t)
(progn
(aset rmail-summary-currently-displayed-msgs msg ?n) nil)))))
;;;###autoload ;;;###autoload
(defun rmail-summary () (defun rmail-summary ()
"Display a summary of all messages, one line per message." "Display a summary of all messages, one line per message."
@ -274,33 +309,52 @@ Setting this option to nil might speed up the generation of summaries."
(rmail-new-summary "All" '(rmail-summary) nil)) (rmail-new-summary "All" '(rmail-summary) nil))
;;;###autoload ;;;###autoload
(defun rmail-summary-by-labels (labels) (defun rmail-summary-by-labels (labels &optional keep-filtering)
"Display a summary of all messages with one or more LABELS. "Display a summary of all messages with one or more LABELS.
LABELS should be a string containing the desired labels, separated by commas." LABELS should be a string containing the desired labels, separated by commas.
(interactive "sLabels to summarize by: ") If KEEP-FILTERING is non-nil (interactively, the prefix argument), operate
on the current summary instead of all mail messages."
(interactive "sLabels to summarize by: \nP")
(if (string= labels "") (if (string= labels "")
(setq labels (or rmail-last-multi-labels (setq labels (or rmail-last-multi-labels
(error "No label specified")))) (error "No label specified"))))
(setq rmail-last-multi-labels labels) (setq rmail-last-multi-labels labels)
(if keep-filtering
(rmail-summary-fill-displayed-messages))
(rmail-new-summary (concat "labels " labels) (rmail-new-summary (concat "labels " labels)
(list 'rmail-summary-by-labels labels) (list 'rmail-summary-by-labels labels keep-filtering)
'rmail-message-labels-p (if keep-filtering
(lambda (msg l)
(and (= (aref rmail-summary-currently-displayed-msgs msg)
?y)
(rmail-message-labels-p msg l)))
'rmail-message-labels-p)
(concat " \\(" (concat " \\("
(mail-comma-list-regexp labels) (mail-comma-list-regexp labels)
"\\)\\(,\\|\\'\\)"))) "\\)\\(,\\|\\'\\)")))
;;;###autoload ;;;###autoload
(defun rmail-summary-by-recipients (recipients &optional primary-only) (defun rmail-summary-by-recipients (recipients &optional primary-only keep-filtering)
"Display a summary of all messages with the given RECIPIENTS. "Display a summary of all messages with the given RECIPIENTS.
Normally checks the To, From and Cc fields of headers; Normally checks the To, From and Cc fields of headers;
but if PRIMARY-ONLY is non-nil (prefix arg given), but if PRIMARY-ONLY is non-nil (prefix arg given),
only look in the To and From fields. only look in the To and From fields.
RECIPIENTS is a regular expression." RECIPIENTS is a regular expression.
If KEEP-FILTERING is non-nil, operate on the current summary
instead of all mail messages."
(interactive "sRecipients to summarize by: \nP") (interactive "sRecipients to summarize by: \nP")
(if keep-filtering
(rmail-summary-fill-displayed-messages))
(rmail-new-summary (rmail-new-summary
(concat "recipients " recipients) (concat "recipients " recipients)
(list 'rmail-summary-by-recipients recipients primary-only) (list 'rmail-summary-by-recipients recipients primary-only keep-filtering)
'rmail-message-recipients-p recipients primary-only)) (if keep-filtering
(lambda (msg r)
(and (= (aref rmail-summary-currently-displayed-msgs msg)
?y)
(rmail-message-recipients-p msg r)))
'rmail-message-recipients-p)
recipients primary-only))
(defun rmail-message-recipients-p (msg recipients &optional primary-only) (defun rmail-message-recipients-p (msg recipients &optional primary-only)
(rmail-apply-in-message msg 'rmail-message-recipients-p-1 (rmail-apply-in-message msg 'rmail-message-recipients-p-1
@ -318,19 +372,28 @@ RECIPIENTS is a regular expression."
;; Also, the optional WHOLE-MESSAGE argument of r-s-by-topic would ;; Also, the optional WHOLE-MESSAGE argument of r-s-by-topic would
;; seem more natural here. ;; seem more natural here.
;;;###autoload ;;;###autoload
(defun rmail-summary-by-regexp (regexp) (defun rmail-summary-by-regexp (regexp &optional keep-filtering)
"Display a summary of all messages according to regexp REGEXP. "Display a summary of all messages according to regexp REGEXP.
If the regular expression is found in the header of the message If the regular expression is found in the header of the message
\(including in the date and other lines, as well as the subject line), \(including in the date and other lines, as well as the subject line),
Emacs will list the message in the summary." Emacs will list the message in the summary.
(interactive "sRegexp to summarize by: ") If KEEP-FILTERING is non-nil (interactively, the prefix argument), operate
on the current summary instead of all mail messages."
(interactive "sRegexp to summarize by: \nP")
(if (string= regexp "") (if (string= regexp "")
(setq regexp (or rmail-last-regexp (setq regexp (or rmail-last-regexp
(error "No regexp specified")))) (error "No regexp specified"))))
(setq rmail-last-regexp regexp) (setq rmail-last-regexp regexp)
(if keep-filtering
(rmail-summary-fill-displayed-messages))
(rmail-new-summary (concat "regexp " regexp) (rmail-new-summary (concat "regexp " regexp)
(list 'rmail-summary-by-regexp regexp) (list 'rmail-summary-by-regexp regexp keep-filtering)
'rmail-message-regexp-p (if keep-filtering
(lambda (msg r)
(and (= (aref rmail-summary-currently-displayed-msgs msg)
?y)
(rmail-message-regexp-p msg r)))
'rmail-message-regexp-p)
regexp)) regexp))
(defun rmail-message-regexp-p (msg regexp) (defun rmail-message-regexp-p (msg regexp)
@ -363,11 +426,14 @@ Emacs will list the message in the summary."
(rmail--decode-and-apply 're-search-forward regexp nil t))) (rmail--decode-and-apply 're-search-forward regexp nil t)))
;;;###autoload ;;;###autoload
(defun rmail-summary-by-topic (subject &optional whole-message) (defun rmail-summary-by-topic (subject &optional whole-message keep-filtering)
"Display a summary of all messages with the given SUBJECT. "Display a summary of all messages with the given SUBJECT.
Normally checks just the Subject field of headers; but with prefix Normally checks just the Subject field of headers; but when
argument WHOLE-MESSAGE is non-nil, looks in the whole message. WHOLE-MESSAGE is non-nil (interactively, prefix argument),
SUBJECT is a regular expression." looks in the whole message.
SUBJECT is a regular expression.
If KEEP-FILTERING is non-nil, operate on the current summary instead
of all mail messages."
(interactive (interactive
;; We quote the default subject, because if it contains regexp ;; We quote the default subject, because if it contains regexp
;; special characters (eg "?"), it can fail to match itself. (Bug#2333) ;; special characters (eg "?"), it can fail to match itself. (Bug#2333)
@ -376,10 +442,18 @@ SUBJECT is a regular expression."
(if subject ", default current subject" "") (if subject ", default current subject" "")
"): "))) "): ")))
(list (read-string prompt nil nil subject) current-prefix-arg))) (list (read-string prompt nil nil subject) current-prefix-arg)))
(if keep-filtering
(rmail-summary-fill-displayed-messages))
(rmail-new-summary (rmail-new-summary
(concat "about " subject) (concat "about " subject)
(list 'rmail-summary-by-topic subject whole-message) (list 'rmail-summary-by-topic subject whole-message keep-filtering)
'rmail-message-subject-p subject whole-message)) (if keep-filtering
(lambda (msg s wo)
(and (= (aref rmail-summary-currently-displayed-msgs msg)
?y)
(rmail-message-subject-p msg s wo)))
'rmail-message-subject-p)
subject whole-message))
(defun rmail-message-subject-p (msg subject &optional whole-message) (defun rmail-message-subject-p (msg subject &optional whole-message)
(if whole-message (if whole-message
@ -389,10 +463,12 @@ SUBJECT is a regular expression."
(string-match subject (rmail-simplified-subject msg)))) (string-match subject (rmail-simplified-subject msg))))
;;;###autoload ;;;###autoload
(defun rmail-summary-by-senders (senders) (defun rmail-summary-by-senders (senders &optional keep-filtering)
"Display a summary of all messages whose \"From\" field matches SENDERS. "Display a summary of all messages whose \"From\" field matches SENDERS.
SENDERS is a regular expression. The default for SENDERS matches the SENDERS is a regular expression. The default for SENDERS matches the
sender of the current message." sender of the current message.
If KEEP-FILTERING is non-nil (interactively, the prefix argument), operate
on the current summary instead of all mail messages."
(interactive (interactive
(let* ((def (rmail-get-header "From")) (let* ((def (rmail-get-header "From"))
;; We quote the default argument, because if it contains regexp ;; We quote the default argument, because if it contains regexp
@ -401,10 +477,20 @@ sender of the current message."
(prompt (concat "Senders to summarize by (regexp" (prompt (concat "Senders to summarize by (regexp"
(if sender ", default this message's sender" "") (if sender ", default this message's sender" "")
"): "))) "): ")))
(list (read-string prompt nil nil sender)))) (list (read-string prompt nil nil sender)
current-prefix-arg)))
(if keep-filtering
(rmail-summary-fill-displayed-messages))
(rmail-new-summary (rmail-new-summary
(concat "senders " senders) (concat "senders " senders)
(list 'rmail-summary-by-senders senders) 'rmail-message-senders-p senders)) (list 'rmail-summary-by-senders senders keep-filtering)
(if keep-filtering
(lambda (msg s)
(and (= (aref rmail-summary-currently-displayed-msgs msg)
?y)
(rmail-message-senders-p msg s)))
'rmail-message-senders-p)
senders))
(defun rmail-message-senders-p (msg senders) (defun rmail-message-senders-p (msg senders)
(string-match senders (or (rmail-get-header "From" msg) ""))) (string-match senders (or (rmail-get-header "From" msg) "")))