summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJSDurand <mmemmew@gmail.com>2021-12-26 14:52:04 +0800
committerJSDurand <mmemmew@gmail.com>2021-12-26 14:52:04 +0800
commit1ebd6269fc1ec4d5a4bf2296d6ed32da7db1f75c (patch)
treef7307369db2ab0335ae41136cbf6f7a79411c6eb
parent20f73b0beebc71466b7be0dea537ab05e8f7be5c (diff)
gnus-conf: Integration with notmuch
-rw-r--r--gnus-conf.el197
1 files changed, 175 insertions, 22 deletions
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