aboutsummaryrefslogtreecommitdiff
path: root/.emacs.d/rul-lisp/packages
diff options
context:
space:
mode:
Diffstat (limited to '.emacs.d/rul-lisp/packages')
-rw-r--r--.emacs.d/rul-lisp/packages/rul-mail.el82
1 files changed, 70 insertions, 12 deletions
diff --git a/.emacs.d/rul-lisp/packages/rul-mail.el b/.emacs.d/rul-lisp/packages/rul-mail.el
index 4af5cc7..a16614e 100644
--- a/.emacs.d/rul-lisp/packages/rul-mail.el
+++ b/.emacs.d/rul-lisp/packages/rul-mail.el
@@ -51,21 +51,79 @@
(interactive (notmuch-search-interactive-region))
(notmuch-search-tag (list "+spam" "-inbox" "-unread") beg end)))
+ ;; Unspam: restore false-positives back to INBOX. Unlike archive, the file
+ ;; move has to happen inline — the hook's path:current/Spam/** rule would
+ ;; otherwise re-tag +spam on the next post-new, reverting the user's action.
+ ;; Move the file BEFORE changing tags so an interleaved post-new from mbsync
+ ;; can't re-apply +spam between our tag change and our rename.
+ (defun rul/notmuch-move-to-inbox (query)
+ "Move files matching QUERY that live under spam/trash maildirs into
+~/mail/INBOX/cur/ with fresh uuid names. Reindex if any moved."
+ (let ((inbox-cur (expand-file-name "~/mail/INBOX/cur/"))
+ (spam-paths "(path:current/Spam/** or path:current/spam/** or path:current/Trash/** or path:current/trash/**)")
+ (count 0))
+ ;; Spam/trash are in search.exclude_tags, so include excluded files here.
+ (dolist (f (process-lines "notmuch" "search" "--exclude=false" "--output=files"
+ (concat "(" query ") and " spam-paths)))
+ (when (file-exists-p f)
+ (let* ((base (file-name-nondirectory f))
+ (colon (cl-position ?: base :from-end t))
+ (suffix (if colon (substring base colon) ""))
+ (uuid (string-trim (shell-command-to-string "uuidgen"))))
+ (rename-file f (concat inbox-cur uuid suffix))
+ (cl-incf count))))
+ (when (> count 0)
+ (call-process "notmuch" nil nil nil "new" "--no-hooks"))
+ count))
+
+ (defun rul/notmuch-search-unspam (&optional beg end)
+ "Restore threads from spam/trash: move files back to INBOX first (so a
+racing post-new can't re-tag +spam based on path), then tag -spam -trash
+-killed +inbox +unread, then refresh."
+ (interactive (notmuch-search-interactive-region))
+ (let ((tids (save-excursion
+ (goto-char beg)
+ (cl-loop while (< (point) end)
+ collect (notmuch-search-find-thread-id)
+ do (forward-line 1)))))
+ (dolist (tid tids)
+ (rul/notmuch-move-to-inbox (concat "thread:" tid)))
+ (notmuch-search-tag (list "-spam" "-trash" "-killed" "+inbox" "+unread") beg end))
+ (notmuch-refresh-this-buffer))
+
+ (defun rul/notmuch-show-unspam ()
+ "Restore current message from spam/trash back to INBOX."
+ (interactive)
+ (let ((mid (notmuch-show-get-message-id)))
+ (rul/notmuch-move-to-inbox mid)
+ (notmuch-show-tag (list "-spam" "-trash" "-killed" "+inbox" "+unread")))
+ (notmuch-refresh-this-buffer))
+
+ (define-key notmuch-search-mode-map "U" 'rul/notmuch-search-unspam)
+ (define-key notmuch-show-mode-map "U" 'rul/notmuch-show-unspam)
+
;; Archive
+ ;; Stock `a` applies notmuch-archive-tags but doesn't re-run the search, so
+ ;; the thread stays visible in the tag:inbox buffer with stale results. Wrap
+ ;; both `a` and `A` to refresh the buffer so the thread drops out of view.
(setq notmuch-archive-tags (list "-inbox" "+archive"))
- (define-key notmuch-show-mode-map "A"
- (lambda ()
- "archive"
- (interactive)
- (notmuch-show-tag (list "+archive" "-inbox" "-unread"))
- (notmuch-refresh-this-buffer)))
- (define-key notmuch-search-mode-map "A"
- (lambda (&optional beg end)
- "archive thread"
- (interactive (notmuch-search-interactive-region))
- (notmuch-search-tag (list "+archive" "-inbox" "-unread") beg end)
- (notmuch-refresh-this-buffer)))
+ (defun rul/notmuch-search-archive (&optional unarchive beg end)
+ "Archive threads and refresh so they drop out of the inbox view."
+ (interactive (cons current-prefix-arg (notmuch-search-interactive-region)))
+ (notmuch-search-archive-thread unarchive beg end)
+ (notmuch-refresh-this-buffer))
+
+ (defun rul/notmuch-show-archive ()
+ "Archive the current message with +archive -inbox -unread and refresh."
+ (interactive)
+ (notmuch-show-tag (list "+archive" "-inbox" "-unread"))
+ (notmuch-refresh-this-buffer))
+
+ (define-key notmuch-search-mode-map "a" 'rul/notmuch-search-archive)
+ (define-key notmuch-search-mode-map "A" 'rul/notmuch-search-archive)
+ (define-key notmuch-show-mode-map "a" 'rul/notmuch-show-archive)
+ (define-key notmuch-show-mode-map "A" 'rul/notmuch-show-archive)
;; Mark as read
(define-key notmuch-search-mode-map "r"
nihil fit ex nihilo