From 1ebd6269fc1ec4d5a4bf2296d6ed32da7db1f75c Mon Sep 17 00:00:00 2001 From: JSDurand Date: Sun, 26 Dec 2021 14:52:04 +0800 Subject: gnus-conf: Integration with notmuch --- gnus-conf.el | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 175 insertions(+), 22 deletions(-) (limited to 'gnus-conf.el') diff --git a/gnus-conf.el b/gnus-conf.el index 97a3d4a..7c248a8 100644 --- a/gnus-conf.el +++ b/gnus-conf.el @@ -24,6 +24,8 @@ ;;; Code: +;;; Select methods + (require 'message) (require 'gnus) (require 'epa) @@ -115,25 +117,176 @@ ;;; Update by mu4e (autoload 'mu4e-update-mail-and-index "mu4e-utils") +(autoload 'load-config "init") -;; This does not seem to be a good idea. I just keep it here as a -;; reference. +(defalias 'durand-update-mail #'mu4e-update-mail-and-index) -(defun durand-update-mail (&optional arg) - "Update mails. -If ARG is non-nil, also fetch mails from emacs-devel mailing -list." - (interactive "P") - (mu4e-update-mail-and-index nil) - (cond - (arg - (gnus-agent-toggle-plugged t) - (gnus-group-get-new-news) - (gnus-agent-toggle-plugged nil)))) +;; Let notmuch know about the changes automatically. + +(load-config "notmuch-conf.el") + +(require 'notmuch) -(define-key gnus-group-mode-map (vector 117) #'mu4e-update-mail-and-index) +(add-hook 'mu4e-index-updated-hook #'notmuch-poll) + +(define-key gnus-group-mode-map (vector 117) #'durand-update-mail) (define-key global-map (vector ?\C-c ?g) 'gnus) +;;; Use notmuch to search mails + +;;;; Helper function to convert file name to article numbers + +(defun durand-gnus-file-name-to-article-number (filename) + "Convert FILENAME to the article number. +An article number is something Gnus uses internally to identity +malis. This is not simply deduced from the file names. At least +I do not know how to do so. The only way that I know of is to +consult information stored in a structure called +\"nnmaildir--grp\". The function +`nnmaildir-base-name-to-article-number' does this conveniently +for us. + +Note that this function is tailored to my specific directory +structure used to store mails. Different mail directory +structures need different functions. + +Perhaps one can consult `nnmaildir--servers' to find all +available servers, and then find which server corresponds to the +given file name, and then loop through the groups in the server +to determine the corresponding group. But I have few servers and +groups, so that is a generalization I do not need, at present." + (let* ((filebase (file-name-nondirectory filename)) + (server + (cond + ((string-match-p "mbsync/mymail/" filename) "durand") + ("gmail"))) + (group + (cond + ((string-match-p "INBOX" filename) "private") + ("sent")))) + (vector + (format "nnmaildir+%s:%s" server group) + (nnmaildir-base-name-to-article-number + (replace-regexp-in-string ":.*$" "" filebase) + group server) + 100))) + +;;;; Query function + +(defvar durand-gnunque-temp-buffer "*gnunque-temp-buffer*" + "A temporary buffer used by `durand-gnuuque' to collect +information.") + +(defalias 'durand-gnunque #'durand-gnus-notmuch-query) + +(defun durand-gnus-notmuch-query (query) + "Search mails by QUERY." + (interactive "MSearch mails by: ") + (let ((original-query query) + (query (split-string query " ")) + (buffer (get-buffer-create durand-gnunque-temp-buffer)) + results) + ;; obtain the thread IDs of the matching mails + (setq + results + (apply + #'process-lines + "notmuch" "search" "--output=threads" + "--format=text" + query)) + ;; disable recording undo information + (with-current-buffer buffer + (setq buffer-undo-list t)) + ;; collect all message files of the threads + (setq + results + (apply + #'append + (mapcar + (lambda (thread) + (with-current-buffer buffer + (delete-region (point-min) (point-max)) + (call-process + "notmuch" + nil + t + nil + "show" "--body=false" + thread) + (goto-char (point-min)) + (let (res) + (while (re-search-forward "filename:" nil t) + (setq res + (cons + (buffer-substring-no-properties + (point) (line-end-position)) + res))) + res))) + results))) + (kill-buffer buffer) + ;; convert message file names to "article numbers" that Gnus uses + ;; internally + (setq + results + (apply + #'vector + (mapcar #'durand-gnus-file-name-to-article-number results))) + ;; finally we can read the articles + (gnus-group-read-ephemeral-group + (concat "nnselect-" (message-unique-id)) + (list (intern "nnselect") "nnselect") + nil (cons (current-buffer) gnus-current-window-configuration) + nil nil + (list (cons 'nnselect-specs + (list (cons 'nnselect-function #'identity) + (cons 'nnselect-args results))))))) + +;;;; Proper exit + +(defun durand-gnunque-exit-hook () + "This function is called in `gnus-exit-group-hook'. +Since an nnselect virtual group would be unfeasible after we exit +the summary buffer, unless we run the query again, there would be +no loss in removing this group from other variables." + (cond + ((string-match-p (rx bos "nnselect") gnus-newsgroup-name) + (remhash gnus-newsgroup-name gnus-active-hashtb)))) + +(add-hook 'gnus-summary-exit-hook #'durand-gnunque-exit-hook) + +;;;; Save queries to functions + +(defmacro durand-gnus-save-query (name query) + "Save QUERY as a function named NAME." + (declare (indent 1)) + (list + 'defun (intern (format "durand-gnus-view-%s" name)) + nil + (format "Run the query: %s" query) + (list 'interactive) + (list 'durand-gnunque query))) + +;; Protesilaos' mails +(durand-gnus-save-query "prot" "from:prot* or to:prot*") + +;; From my professors +(durand-gnus-save-query "minglung" + "from:Ming-Lun.Hsieh* or to:Ming-Lun.Hsieh*") +(durand-gnus-save-query "tan" "from:tan* or to:tan*") + +;; friends +(durand-gnus-save-query "friends" + "from:r03221010* or to:r03221010* \ +or from:bill821230bill* or to:bill821230bill* \ +or from:u1991123* or to:u1991123*") + +;;;; bind in gnus group buffer + +(define-key gnus-group-mode-map (vector ?v ?p) #'durand-gnus-view-prot) +(define-key gnus-group-mode-map (vector ?v ?m) #'durand-gnus-view-minglung) +(define-key gnus-group-mode-map (vector ?v ?t) #'durand-gnus-view-tan) +(define-key gnus-group-mode-map (vector ?v ?f) #'durand-gnus-view-friends) + ;;; user settings (setq gnus-ignored-from-addresses @@ -150,7 +303,7 @@ list." (setq mail-user-agent 'gnus-user-agent) -(setq read-mail-command 'gnus) +(setq read-mail-command #'gnus) (set 'gnus-novice-user nil) @@ -378,8 +531,6 @@ ARGS is the list of arguments passed to FN." (setq gnus-activate-level 4) -(setq gnus-ignored-from-addresses "mmemmew\\.com") - ;;; move between topics (define-key gnus-group-mode-map [?\M-n] 'gnus-topic-goto-next-topic) @@ -422,9 +573,9 @@ ARGS is the list of arguments passed to FN." (require 'gnus-art) -(define-key gnus-article-mode-map [?z ?t] 'recenter-to-top) -(define-key gnus-article-mode-map [?z ?b] 'recenter-to-bottom) -(define-key gnus-article-mode-map [?z ?z] 'recenter-to-middle) +;; (define-key gnus-article-mode-map [?z ?t] 'recenter-to-top) +;; (define-key gnus-article-mode-map [?z ?b] 'recenter-to-bottom) +;; (define-key gnus-article-mode-map [?z ?z] 'recenter-to-middle) (define-key gnus-article-mode-map [?o] 'gnus-mime-inline-part) (setq gnus-article-sort-functions @@ -444,7 +595,8 @@ ARGS is the list of arguments passed to FN." (setq gnus-suppress-duplicates t) (setq gnus-summary-goto-unread 'never) (setq gnus-summary-to-prefix "To: ") -(setq gnus-summary-line-format "%U%R%z %-16,16&user-date; %*%(%-30,30a %B%s%)\n") +(setq gnus-summary-line-format + "%U%R%z %-16,16&user-date; %*%(%-30,30a %B%s%)\n") (setq gnus-summary-mode-line-format "%p") (setq gnus-summary-make-false-root 'adopt) (setq gnus-sum-thread-tree-false-root "─┬> ") @@ -453,7 +605,8 @@ ARGS is the list of arguments passed to FN." (setq gnus-sum-thread-tree-root "") (setq gnus-sum-thread-tree-single-leaf "└─> ") (setq gnus-sum-thread-tree-vertical "│") -(setq gnus-summary-thread-gathering-function 'gnus-gather-threads-by-subject) +(setq gnus-summary-thread-gathering-function + #'gnus-gather-threads-by-subject) ;;; disable auto-save for newrc file -- cgit v1.2.3-18-g5258