From 66988068b79ca5677c4c7f4fed66a815a1b33d53 Mon Sep 17 00:00:00 2001 From: jao Date: Mon, 8 Nov 2021 00:47:34 +0000 Subject: gnus + nnml + notmuch --- email.org | 36 ++++++- gnus.org | 237 +++++++++++++++++++++++++----------------- init.org | 2 +- lib/net/jao-maildir.el | 28 +++-- lib/themes/jao-light-theme.el | 11 +- notmuch.org | 27 ----- 6 files changed, 202 insertions(+), 139 deletions(-) diff --git a/email.org b/email.org index a9bc09c..8dec9ac 100644 --- a/email.org +++ b/email.org @@ -1,5 +1,5 @@ #+property: header-args:emacs-lisp :lexical t :tangle yes :comments yes :results silent :shebang ";; -*- lexical-binding: t; -*-" :tangle-mode (identity #o644) -#+title: email handling (message mode, bbdb, notmuch) +#+title: email handling (message mode, bbdb, gnus, notmuch) #+auto_tangle: t * message mode @@ -209,7 +209,6 @@ #+end_src * multipart html renderer #+begin_src emacs-lisp - (defun jao-w3m-html-renderer (handle) (let ((w3m-message-silent t) (mm-w3m-safe-url-regexp nil)) @@ -220,7 +219,14 @@ (shr-use-colors nil)) (mm-shr handle)))))) + (defun jao-shr-html-renderer (handle) + (let ((shr-use-fonts nil) + (shr-use-colors nil) + (fill-column 120)) + (mm-shr handle))) + ;; (setq mm-text-html-renderer #'jao-w3m-html-renderer) + (setq mm-text-html-renderer #'jao-shr-html-renderer) #+end_src * bbdb #+begin_src emacs-lisp @@ -346,3 +352,29 @@ (when (eq jao-afio-mail-function 'notmuch) (jao-load-org "notmuch")) #+end_src +*** consult + #+begin_src emacs-lisp + (jao-load-path "consult-notmuch") + (require 'consult-notmuch) + (consult-customize consult-notmuch :preview-key 'any) + + (defvar jao-consult-notmuch-history nil) + + (defun jao-consult-notmuch-folder (&optional tree folder) + (interactive "P") + (let* ((root "~/var/mail/") + (folder (if folder + (file-name-as-directory folder) + (completing-read "Folder: " + jao-mailbox-folders + nil nil nil + jao-consult-notmuch-history + "."))) + (folder (replace-regexp-in-string "/\\(.\\)" ".\\1" folder)) + (init (read-string "Initial query: ")) + (init (format "folder:/%s/ %s" folder init))) + (if tree (consult-notmuch-tree init) (consult-notmuch init)))) + + (with-eval-after-load "notmuch-hello" + (define-key notmuch-hello-mode-map "f" #'jao-consult-notmuch-folder)) + #+end_src diff --git a/gnus.org b/gnus.org index ae52fe7..1a3e176 100644 --- a/gnus.org +++ b/gnus.org @@ -11,7 +11,7 @@ (defvar jao-gnus-use-gmane nil) (defvar jao-gnus-use-nnml nil) (defvar jao-gnus-use-maildirs nil) - (defvar jao-gnus-use-nnnm t) + (defvar jao-gnus-use-nnnm nil) #+end_src * Startup and kill #+begin_src emacs-lisp @@ -121,21 +121,32 @@ [[info:gnus#Searching][info:gnus#Searching]] #+begin_src emacs-lisp (setq gnus-search-use-parsed-queries t - jao-gnus-search-prefix (expand-file-name "~/var/mail/")) + gnus-search-notmuch-raw-queries-p nil) (with-eval-after-load "gnus-search" - (add-to-list 'gnus-search-expandable-keys "list-id")) + (add-to-list 'gnus-search-expandable-keys "list") + (add-to-list 'gnus-search-expandable-keys "Newsgroups") + + (cl-defmethod gnus-search-transform-expression ((engine gnus-search-notmuch) + (expr (head list))) + (message "Transforming: %S" expr) + (format "List:%s" (gnus-search-transform-expression engine (cdr expr))))) + + (defun jao-gnus--notmuch-engine (prefix config) + (let ((prefix (file-name-as-directory (expand-file-name prefix "~"))) + (config (expand-file-name config gnus-home-directory))) + `(gnus-search-engine gnus-search-notmuch + (remove-prefix ,prefix) + (config-file ,config)))) #+end_src * News server #+begin_src emacs-lisp (setq gnus-select-method - (cond (jao-gnus-use-leafnode - '(nntp "localhost" - (gnus-search-engine - gnus-search-notmuch - (remove-prefix "/home/jao/var/news/") - (config-file "/home/jao/.notmuch-config-news")))) - (jao-gnus-use-gmane '(nntp "news.gmane.io")) - (t '(nnnil "")))) + (cond + (jao-gnus-use-leafnode + `(nntp "localhost" + ,(jao-gnus--notmuch-engine "var/news" "notmuch-news.config"))) + (jao-gnus-use-gmane '(nntp "news.gmane.io")) + (t '(nnnil "")))) (setq gnus-secondary-select-methods '()) @@ -160,47 +171,62 @@ '(nnimap "gandi" (nnimap-address "mail.gandi.net")))) #+end_src * Local mail servers - #+begin_src emacs-lisp - (setq mail-sources nil - gnus-message-archive-group nil - nnimap-quirks nil) - - (setq nnml-get-new-mail t - nnmail-treat-duplicates 'delete - nnmail-scan-directory-mail-source-once t - nnmail-cache-accepted-message-ids t - nnmail-message-id-cache-length 50000 - nnmail-cache-ignore-groups ".*\\(trove\\.\\|feeds\\.\\|spamish\\).*" - nnmail-split-fancy-with-parent-ignore-groups nil - nnmail-crosspost t) - - (setq nnmail-resplit-incoming t - nnmail-mail-splitting-decodes t - nnmail-split-methods 'nnmail-split-fancy) - - (when jao-gnus-use-nnml - (add-to-list 'gnus-secondary-select-methods '(nnml ""))) - - (when jao-gnus-use-maildirs - (add-to-list 'gnus-secondary-select-methods - `(nnmaildir "bml" (directory "~/var/mail/bigml/") - (gnus-search-engine gnus-search-notmuch - (remove-prefix - "/home/jao/var/mail/")))) - (add-to-list 'gnus-secondary-select-methods - '(nnmaildir "jao" (directory "~/var/mail/jao/") - (gnus-search-engine gnus-search-notmuch - (remove-prefix - "/home/jao/var/mail/")))) - (add-to-list 'gnus-secondary-select-methods - '(nnmaildir "feeds" (directory "~/var/mail/feeds/") - (gnus-search-engine gnus-search-notmuch - (remove-prefix - "/home/jao/var/mail/"))))) +*** nnml + #+begin_src emacs-lisp + (setq mail-sources '((group)) + gnus-message-archive-group nil + nnimap-quirks nil) + + (setq nnml-get-new-mail t + nnmail-treat-duplicates 'delete + nnmail-scan-directory-mail-source-once nil + nnmail-cache-accepted-message-ids t + nnmail-message-id-cache-length 50000 + nnmail-split-fancy-with-parent-ignore-groups nil + nnmail-use-long-file-names t + nnmail-crosspost t) + + (setq nnmail-resplit-incoming nil + nnmail-mail-splitting-decodes t + nnmail-split-methods 'nnmail-split-fancy) + + (when jao-gnus-use-nnml + (let ((mails (file-name-as-directory mail-source-directory))) + (add-to-list + 'gnus-secondary-select-methods + `(nnml "" ,(jao-gnus--notmuch-engine mails "notmuch.config"))))) + + (defun jao-gnus-nnml--add-maildir (dir) + (dolist (f (directory-files (concat "~/var/mail/" dir) t "[^\\.]")) + (add-to-list 'gnus-parameters + `(,(concat "nnml:" dir "." (file-name-base f)) + (mail-source . (maildir :path ,f))) + t))) - (when (and jao-gnus-use-nnnm (require 'nnnm nil t)) - (add-to-list 'gnus-secondary-select-methods '(nnnm ""))) - #+end_src + #+end_src +*** maildirs + #+begin_src emacs-lisp + (when jao-gnus-use-maildirs + (defun jao-gnus--maildir (dir) + (let ((root (concat "/tmp/mboxes/" dir "/")) + (config (concat "~/.notmuch-config-" dir))) + (add-to-list + 'gnus-secondary-select-methods + `(nnmaildir ,dir (directory ,root) + (gnus-search-engine gnus-search-notmuch + (remove-prefix ,root) + (config-file ,config)))))) + (jao-gnus--maildir "jao") + (jao-gnus--maildir "feeds") + (jao-gnus--maildir "bigml")) + + #+end_src +*** nnnm + #+begin_src emacs-lisp + (when (and jao-gnus-use-nnnm (require 'nnnm nil t)) + (add-to-list 'gnus-secondary-select-methods '(nnnm ""))) + + #+end_src * Demons and notifications #+begin_src emacs-lisp (setq mail-user-agent 'gnus-user-agent) @@ -217,20 +243,19 @@ (setq gnus-check-new-newsgroups nil) (defvar jao-gnus-tracked-groups - `(("nnimap:bigml/inbox" "B" jao-themes-f00) - ("nnselect:bigml-bugs" "b" jao-themes-error) - ("nnimap:bigml/support" "S" default) - ("nnimap:jao/inbox" "I" jao-themes-f01) - ("nnimap:bigml/[^ibs]" "W" jao-themes-dimm) - ("nnimap:jao" "J" jao-themes-dimm) - ("nnimap:local" "l" jao-themes-dimm) - ("nnimap:feeds/papers" "P" jao-themes-dimm) - ("^gmane\\.emacs\\|nnimap:feeds/emacs" "E" jao-themes-dimm) - (,(format "nnimap:feeds/%s" - (regexp-opt (seq-difference (directory-files "~/var/mail/feeds") - '("emacs" "papers" "trove" "." "..")))) - "F" jao-themes-dimm) - ("^gmane\\.[^e]\\|^gwene" "N" jao-themes-dimm))) + (let ((feeds (seq-difference (directory-files "~/var/mail/feeds") + '("emacs" "papers" "trove" "." "..")))) + `(("nnml:bigml.inbox" "B" jao-themes-f00) + ("nnselect:bigml.bugs" "b" jao-themes-error) + ("nnml:bigml.support" "S" default) + ("nnml:jao.drivel" "I" jao-themes-f01) + ("nnml:bigml.[^ibs]" "W" jao-themes-dimm) + ("nnml:jao.[^d]" "J" jao-themes-dimm) + ("nnml:local" "l" jao-themes-dimm) + ("nnml:feeds.papers" "P" jao-themes-dimm) + ("^gmane\\.emacs\\|nnml:feeds.emacs" "E" jao-themes-dimm) + (,(format "nnml:feeds.%s" (regexp-opt feeds)) "F" jao-themes-dimm) + ("^gmane\\.[^e]\\|^gwene" "N" jao-themes-dimm)))) (defun jao-gnus--unread-counts () (seq-reduce (lambda (r g) @@ -311,15 +336,15 @@ #+begin_src emacs-lisp (with-eval-after-load "randomsig" (with-eval-after-load "gnus-sum" - (define-key gnus-summary-save-map "-" - 'gnus/randomsig-summary-read-sig))) + (define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig))) #+end_src *** notmuch -> gnus #+begin_src emacs-lisp (defun jao-notmuch-goto-message-in-gnus () "Open a summary buffer containing the current notmuch article." (interactive) - (let ((group (jao-maildir-file-to-group (notmuch-show-get-filename))) + (let ((group (jao-maildir-file-to-group (notmuch-show-get-filename) + mail-source-directory)) (message-id (replace-regexp-in-string "^id:" "" (notmuch-show-get-message-id)))) @@ -386,6 +411,12 @@ (define-key gnus-group-mode-map "zz" #'jao-consult-notmuch-folder) (define-key gnus-group-mode-map "zZ" #'consult-notmuch) + (jao-transient-major-mode gnus-group + ["Search" + ("zc" "consult search" consult-notmuch) + ("zf" "consult folder search" jao-consult-notmuch-folder) + ("g" "gnus search" gnus-group-read-ephemeral-search-group)]) + (defun jao-gnus--first-group () (when (derived-mode-p 'gnus-group-mode) (gnus-group-first-unread-group))) @@ -398,32 +429,42 @@ (setq gnus-permanently-visible-groups "^nnselect:.*") (setq gnus-parameters - `(("^nnnm:jao/.*" - (jao-gnus--trash-group "nnnm:jao/trash") - (jao-gnus--spam-group "nnnm:jao/spam") - (jao-gnus--archiving-group "nnnm:jao/trove")) - ("^nnnm:\\(jao\\|bigml\\)/\\(trash\\|spam\\)" + `(("nnml:local" + (auto-expire . t) + (total-expire . t) + (expiry-wait . 1) + (expiry-target . delete) + (mail-source . (file :path "/var/mail/jao"))) + ("nnml:jao\\..*" + (jao-gnus--trash-group "nnml:jao.trash") + (jao-gnus--spam-group "nnml:jao.spam") + (jao-gnus--archiving-group "nnml:jao.trove")) + ("nnml:\\(jao\\|bigml\\)\\.\\(trash\\|spam\\)" (gcc-self . nil) (auto-expire . t) (total-expire . t) (expiry-wait . 1) (jao-gnus--trash-group nil) (expiry-target . delete)) - ("^nnnm:.*/\\(inbox\\|hacking\\)" + ("^nnml:.*\\.\\(inbox\\|hacking\\)" (gcc-self . t)) - ("^nnnm:bigml/.*" + ("nnml:bigml\\..*" (gcc-self . t) (posting-style (address "jao@bigml.com")) - (jao-gnus--archiving-group "nnnm:bigml/trove") - (jao-gnus--spam-group "nnnm:bigml/spam")) - ;; ("^nnnm:bigml/inbox" - ;; (auto-expire . t) - ;; (total-expire . t) - ;; (expiry-wait . 7) - ;; (expiry-target . "nnnm:bigml/trove")) - ("^nnnm:bigml/support" - (posting-style (address "support@bigml.com"))) - (,(format "^nnnm:bigml/%s" + (jao-gnus--trash-group "nnml:jao.trash") + (jao-gnus--archiving-group "nnml:bigml.trove") + (jao-gnus--spam-group "nnml:bigml.spam")) + ("nnml:bigml.inbox" + (auto-expire . t) + (total-expire . t) + (expiry-wait . 7) + (expiry-target . "nnml:bigml.trove")) + ("nnml:bigml.trove" + (auto-expire . t) + (total-expire . t) + (expiry-target . delete) + (expiry-wait . 365)) + (,(format "^nnml:bigml\\.%s" (regexp-opt '("reports" "deploys" "lists" "drivel"))) (jao-gnus--trash-group nil) (gcc-self . nil) @@ -431,21 +472,24 @@ (total-expire . t) (expiry-wait . 3) (expiry-target . delete)) - ("^nnnm:\\(jao/\\(think\\|drivel\\)\\|local\\|emacs.*\\)" + ("nnml:jao\\.drivel" (auto-expire . t) (total-expire . t) - (expiry-wait . 1) + (expiry-wait . 3) (expiry-target . delete)) - ("^nnnm:feeds/.*" + ("nnml:feeds\\..*" (auto-expire . t) (total-expire . t) (expiry-wait . 7) (expiry-target . delete) - (jao-gnus--archiving-group "nnnm:feeds/trove")) - ("^nnnm:feeds/\\(news\\|emacs\\|prog\\)$" (expiry-wait . 1)) - ("^nnnm:feeds/trove" + (jao-gnus--archiving-group "nnml:feeds/trove")) + ("nnml:feeds\\.\\(news\\|emacs\\|prog\\)$" (expiry-wait . 1)) + ("nnml:feeds\\.trove" (auto-expire . nil) (total-expire . nil)))) + + (dolist (dir '("jao" "bigml" "feeds")) (jao-gnus-nnml--add-maildir dir)) + #+end_src * Summary buffer *** Configuration @@ -666,17 +710,17 @@ (shr-render-region begin (1- (point))))))))) (add-hook 'gnus-part-display-hook 'jao-gnus-remove-anchors) + (defvar-local jao-gnus--images-p nil) (defun jao-gnus-show-images () (interactive) (save-window-excursion (gnus-summary-select-article-buffer) (save-excursion - (let ((pos (next-single-property-change (point) 'w3m-image))) - (if (not pos) - (gnus-article-show-images) - (goto-char pos) - (w3m-toggle-inline-images)))))) + (setq jao-gnus--images-p (not jao-gnus--images-p)) + (if jao-gnus--images-p + (gnus-article-show-images) + (gnus-article-remove-images))))) #+end_src *** Follow links and enclosures #+begin_src emacs-lisp @@ -718,6 +762,7 @@ (define-key gnus-summary-mode-map "V" 'scroll-other-window-down) (define-key gnus-summary-mode-map "X" 'jao-gnus-arXiv-capture) (define-key gnus-summary-mode-map "e" 'jao-gnus-open-enclosure) + (define-key gnus-summary-mode-map "\C-l" nil) (with-eval-after-load "w3m" (define-key gnus-article-mode-map "\C-ci" 'w3m-view-image) diff --git a/init.org b/init.org index 9c347c7..0f17566 100644 --- a/init.org +++ b/init.org @@ -1593,7 +1593,7 @@ #+end_src * Email #+begin_src emacs-lisp - (setq jao-afio-mail-function 'notmuch) + (setq jao-afio-mail-function 'gnus) (jao-load-org "email") #+end_src * PDFs diff --git a/lib/net/jao-maildir.el b/lib/net/jao-maildir.el index d7cd4d6..18a1725 100644 --- a/lib/net/jao-maildir.el +++ b/lib/net/jao-maildir.el @@ -35,6 +35,7 @@ (defvar jao-maildir-tracked-maildirs nil) (defvar jao-maildir-info-string "") (defvar jao-maildir-home (expand-file-name "~/var/mail")) +(defvar jao-maildir-news-home (expand-file-name "~/var/news")) (defgroup jao-maildir-faces nil "Faces" :group 'faces) @@ -152,32 +153,37 @@ (jao-maildir--setup-watches cb)) ;;;###autoload -(defun jao-maildir-file-to-group (file) +(defun jao-maildir-file-to-group (file &optional maildir newsdir) "Calculate the Gnus group name from the given file name. Example: IN: /home/jao/var/mail/jao/foo/cur/1259184569.M4818P3384.localhost,W=6921:2,S - OUT: nnimap:jao/foo + OUT: nnml:jao.foo + + IN: /home/jao/.emacs.d/gnus/Mail/jao.trove/32570, /home/jao/.emacs.d/gnus/Mail/ + OUT: nnml:jao.trove IN: /home/jao/var/mail/gmane/foo/bar/100 - OUT: nntp+localhost:gmane.foo.bar + OUT: nntp:gmane.foo.bar IN: /home/jao/var/mail/bigml/cur/1259176906.M17483P24679.localhost,W=2488:2,S OUT:nnimap:bigml/inbox" (let* ((g (directory-file-name (file-name-directory file))) - (g (replace-regexp-in-string (file-name-as-directory jao-maildir-home) - "" g)) + (g (replace-regexp-in-string + (file-name-as-directory (or maildir jao-maildir-home)) "" g)) + (g (replace-regexp-in-string + (file-name-as-directory (or newsdir jao-maildir-news-home)) "" g)) (nntp (string-match-p "^\\(gmane\\|gwene\\)/" g)) - (g (if nntp - (concat "nntp+localhost:" g) - (replace-regexp-in-string "^\\([^/]+\\)/" "nnimap:\\1/" - (file-name-directory g) t))) - (g (if nntp (replace-regexp-in-string "/" "." g) g)) + (g (cond (nntp (concat "nntp:" g)) + ((file-name-directory g) + (replace-regexp-in-string "^\\([^/]+\\)/" "nnml:\\1/" + (file-name-directory g) t)) + (t (concat "nnml:" g)))) + (g (replace-regexp-in-string "/" "." g)) (g (replace-regexp-in-string "[/.]$" "" g))) (cond ((string-match ":$" g) (concat g "inbox")) (nntp g) (t (replace-regexp-in-string ":\\." ":" g))))) - (provide 'jao-maildir) ;;; jao-maildir.el ends here diff --git a/lib/themes/jao-light-theme.el b/lib/themes/jao-light-theme.el index 98c3d40..2a6c82e 100644 --- a/lib/themes/jao-light-theme.el +++ b/lib/themes/jao-light-theme.el @@ -87,12 +87,19 @@ (diff-hl-delete (c "white" "wheat1")) (fill-column-indicator (c "grey80")) (fringe (c "grey70" nil)) + (gnus-button (p dimm) nul) + (gnus-cite-1 (c "darkslategray" nil)) + (gnus-cite-2 (c "slate gray" nil)) + (gnus-cite-3 (c "slate gray" nil)) + (gnus-cite-4 (c "slate gray" nil)) + (gnus-summary-selected (c green) nbf) + (gnus-summary-cancelled (c "sienna3" nil) st) (header-line (c nil "#efebe7")) (magit-diff-context-highlight (c nil yellow) ex) (magit-diff-hunk-heading-highlight (c nil yellow) it bf) - (mode-line (c "grey30" dimm-background-3) + (mode-line (c "grey30" dimm-background-2) :box (:line-width -1 :color "grey90")) - (mode-line-inactive (c "grey40" dimm-background-4) + (mode-line-inactive (c "grey40" "white") :box (:line-width -1 :color "grey90")) (mode-line-buffer-id (~ default) (c dark-blue-2 nil) nit) (mode-line-emphasis (c green nil)) diff --git a/notmuch.org b/notmuch.org index 8683045..d3d832d 100644 --- a/notmuch.org +++ b/notmuch.org @@ -429,33 +429,6 @@ ("SPC" . jao-notmuch-tree-scroll-or-next) ("M-g" . jao-notmuch-browse-url)))) #+end_src -* consult - #+begin_src emacs-lisp - (jao-load-path "consult-notmuch") - (setq consult-notmuch-authors-width 30) - (require 'consult-notmuch) - (consult-customize consult-notmuch :preview-key 'any) - - (defvar jao-consult-notmuch-history nil) - - (defun jao-consult-notmuch-folder (&optional tree folder) - (interactive "P") - (let* ((root "~/var/mail/") - (folder (if folder - (file-name-as-directory folder) - (completing-read "Folder: " - jao-mailbox-folders - nil nil nil - jao-consult-notmuch-history - "."))) - (folder (replace-regexp-in-string "/\\(.\\)" ".\\1" folder)) - (init (read-string "Initial query: ")) - (init (format "folder:/%s/ %s" folder init))) - (if tree (consult-notmuch-tree init) (consult-notmuch init)))) - - (with-eval-after-load "notmuch-hello" - (define-key notmuch-hello-mode-map "f" #'jao-consult-notmuch-folder)) - #+end_src * org mode Stolen and adapted from [[https://gist.github.com/fedxa/fac592424473f1b70ea489cc64e08911][Fedor Bezrukov]]. #+begin_src emacs-lisp -- cgit v1.2.3