diff options
Diffstat (limited to 'custom/jao-custom-notmuch.el')
| -rw-r--r-- | custom/jao-custom-notmuch.el | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/custom/jao-custom-notmuch.el b/custom/jao-custom-notmuch.el new file mode 100644 index 0000000..ca2664c --- /dev/null +++ b/custom/jao-custom-notmuch.el @@ -0,0 +1,757 @@ +;;; -*- lexical-binding: t; -*- +;;; minibuffer +(defvar jao-notmuch-minibuffer-string "") + +(defvar jao-notmuch-minibuffer-queries + `((:name "" :query "tag:new and not tag:draft" :face jao-themes-f00) + (:name "I" :query "tag:new and tag:jao and tag:inbox" + :face jao-themes-warning) + (:name "W" :query "tag:new and tag:jao and tag:write" + :face jao-themes-warning) + (:name "J" + :query + ,(concat "tag:new and tag:jao and not " + "tag:\"/feeds|local|hacking|draft|inbox|prog|words|write/\"") + :face default) + (:name "H" :query "tag:new and tag:hacking and not tag:\"/emacs/\"") + (:name "E" :query "tag:new and tag:\"/emacs/\"") + (:name "l" :query "tag:new and tag:local") + (:name "F" :query "tag:new and tag:feeds and not tag:\"/emacs/\""))) + +(defvar jao-notmuch-xbar-queries + `((:name "" :query "tag:new and not tag:draft" :face jao-themes-f00) + (:name "i" :query "tag:new and tag:/inbox|write|drivel/" + :face jao-themes-warning))) + +(defsubst jao-notmuch--qstr (c) + (format "%s%s" (plist-get c :name) (plist-get c :count))) + +(defvar jao-notmuch-mac-xbar t) +(defvar jao-notmuch-mac-mail-badge nil) + +(defun jao-notmuch-xbar () + (let ((cnts (notmuch-hello-query-counts jao-notmuch-xbar-queries))) + (jao-shell-exec (format "echo '%s | color=#8b3626 | size=11' >/tmp/xbar" + (mapconcat 'jao-notmuch--qstr cnts " "))))) + +(defun jao-notmuch-notify () + (interactive) + (let* ((cnts (notmuch-hello-query-counts jao-notmuch-minibuffer-queries)) + (mc (jao-d-l + (when jao-notmuch-mac-mail-badge (jao-mac-app-badge "Mail")) + 0)) + (cnts (if (> (or mc 0) 0) (cons `(:name "" :count ,mc) cnts) cnts))) + (setq jao-notmuch-minibuffer-string + (mapconcat (lambda (c) + (propertize (jao-notmuch--qstr c) + 'face (or (plist-get c :face) + 'jao-themes-dimm))) + cnts + " ")) + (jao-when-darwin (when jao-notmuch-mac-xbar (jao-notmuch-xbar))) + (jao-minibuffer-refresh))) + +(defun jao-notmuch-notify-and-update () + (jao-notmuch-notify) + (with-current-buffer "*notmuch-hello*" + (notmuch-hello-update) + (jao-notmuch-hello-first))) + +(when jao-notmuch-enabled + (jao-minibuffer-add-variable 'jao-notmuch-minibuffer-string -20)) + +;;; saved searches +(defvar jao-notmuch--new nil) +(defvar jao-notmuch--new-rx nil) +(defvar jao-notmuch--newa nil) + +(defun jao-notmuch--set-new-search (s) + (setq jao-notmuch--new s + jao-notmuch--new-rx (regexp-quote jao-notmuch--new) + jao-notmuch--newa (concat jao-notmuch--new " AND "))) + +(defun jao-notmuch--q (d0 &optional k d1 qs st) + (let ((q (or (when qs (mapconcat #'identity qs " AND ")) + (concat jao-notmuch--new + (mapconcat (lambda (d) (when d (concat " AND tag:" d))) + (list d0 d1)))))) + (list :name (concat d0 (when (and d1 (not (string= "" d1))) "/") d1) + :key k :query q :search-type (or st 'tree) + :sort-order 'oldest-first))) + +(defun jao-notmuch--q-feed (q) + (apply #'jao-notmuch--q (cons "feeds" q))) + +(defun jao-notmuch-tree-widen-search () + (interactive) + (when-let ((query (notmuch-tree-get-query))) + (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto))) + (notmuch-tree-close-message-window) + (notmuch-tree (string-replace jao-notmuch--newa "" query))))) + +(defun jao-notmuch--widen (s &optional extra) + (let* ((q (plist-get s :query)) + (qs (string-replace jao-notmuch--newa "" q))) + (plist-put (copy-sequence s) :query (concat qs extra)))) + +(defun jao-notmuch-widen-searches (searches &optional extra) + (mapcar (lambda (s) (jao-notmuch--widen s extra)) searches)) + +(defun jao-notmuch-hello--insert-searches (searches title) + (when-let (searches (notmuch-hello-query-counts searches)) + (let* ((cnt (when title + (seq-reduce (lambda (c q) + (+ c (or (plist-get q :count) 0))) + searches + 0))) + (title (if title (format "[ %d %s ]\n\n" cnt title) "\n")) + (notmuch-column-control 1.0)) + (widget-insert (propertize title 'face 'jao-themes-f00)) + (notmuch-hello-insert-buttons searches)))) + +(defun jao-notmuch--replace (lst s) + (let ((name (plist-get s :name))) + (set lst (seq-remove (lambda (s0) (string= name (plist-get s0 :name))) + (symbol-value lst))) + (add-to-list lst s t))) + +(defun jao-notmuch--def-searches (name searches &optional no-save) + (declare (indent 1)) + (unless no-save + (dolist (s searches) + (jao-notmuch--replace 'jao-notmuch-widened-searches + (jao-notmuch--widen s)) + (jao-notmuch--replace 'notmuch-saved-searches s))) + (add-to-list 'notmuch-hello-sections + `(jao-notmuch-hello--insert-searches ,searches ,name) + t)) + +(defvar jao-notmuch-widened-searches nil) + +(defun jao-notmuch-define-searches (s &optional other) + (jao-notmuch--set-new-search s) + + (setq notmuch-hello-sections nil + notmuch-saved-searches nil + jao-notmuch-widened-searches nil) + + (when other (jao-notmuch--def-searches (car other) (cdr other))) + + (jao-notmuch--def-searches "inbox" + `(,(jao-notmuch--q "inbox" "i") + ,(jao-notmuch--q "write" "w") + ,(jao-notmuch--q "drivel" "d") + ,(jao-notmuch--q "trove" "t") + ,(jao-notmuch--q "local" "x"))) + + (jao-notmuch--def-searches "news" + (mapcar 'jao-notmuch--q-feed + '(("fn" "news") + ("fd" "noticias") + ("ff" "fun") + ("fw" "words") + ("fc" "computers") + ("fp" "pens")))) + + (jao-notmuch--def-searches "hacking" + `(,(jao-notmuch--q "hacking" "h") + ,(jao-notmuch--q "prog" "p") + ,@(mapcar 'jao-notmuch--q-feed + '(("lc" "clojure") + ("le" "emacs") + ("lg" "geiser") + ("lh" "haskell") + ("ll" "lisp") + ("ln" "notmuch") + ("lr" "rust") + ("ls" "scheme") + ("lx" "xmobar"))))) + + (jao-notmuch--def-searches "sci" + (mapcar 'jao-notmuch--q-feed + '(("sp" "philosophy") + ("sm" "math") + ("sp" "physics") + ("ss" "sci") + ("sg" "gr-qc") + ("sq" "quant-ph")))) + + (jao-notmuch--def-searches "flags" + (jao-notmuch-widen-searches notmuch-saved-searches " AND tag:flagged") + t) + + (jao-notmuch--def-searches nil + `(,(jao-notmuch--q "flagged" "F" nil '("tag:flagged")))) + + (jao-notmuch--def-searches "today" + `(,(jao-notmuch--q "new" "N" nil (list jao-notmuch--new "not tag:draft")) + ,(jao-notmuch--q "drafts" "D" nil '("tag:draft") t) + ,(jao-notmuch--q "sent" "S" nil '("date:1d.." "tag:sent") t) + ,(jao-notmuch--q "today" "T" nil + '("tag:jao" "date:24h.." + "not tag:\"/(sent|feeds|spam|local)/\"") + t))) + + (jao-notmuch--def-searches "trove" + '((:query "tag:trove" :name "trove" :search-type tree) + (:query "tag:hacking" :name "hacking" :search-type tree) + (:query "not tag:trove and not tag:hacking" :name "untroved" + :search-type tree) + (:query "tag:sent" :name "sent"))) + + (jao-notmuch--def-searches nil + '((:query "*" :name "messages" :search-type tree))) + + ;; (add-to-list 'notmuch-hello-sections 'notmuch-hello-insert-alltags t) + + (when (display-graphic-p) + (add-to-list 'notmuch-hello-sections '(insert " ")))) + +(jao-notmuch-define-searches "tag:\"/^(unread|new)$/\"") + +(defun jao-notmuch-jump-search (&optional widen) + (interactive "P") + (let ((notmuch-saved-searches + (if widen jao-notmuch-widened-searches notmuch-saved-searches))) + (notmuch-jump-search))) + +(defun jao-notmuch--searches-w/new (searches) + (seq-filter (lambda (s) + (string-match-p jao-notmuch--new-rx (plist-get s :query))) + (notmuch-hello-query-counts searches))) + +(defconst jao-notmuch--search-keys '("a" "s" "d" "f" "g" "h" "j" "k" "l")) + +(defun jao-notmuch--index-searches (searches) + (seq-map-indexed (lambda (q i) + (plist-put q :key (or (elt jao-notmuch--search-keys i) + (format "%s" i)))) + searches)) + +(defun jao-notmuch-jump () + "Jump to a saved, non-zero count search." + (interactive) + (if-let* ((searches (jao-notmuch--searches-w/new notmuch-saved-searches))) + (if (= 2 (length searches)) + (let* ((q (seq-remove (lambda (q) (string= "new" (plist-get q :name))) + searches)) + (q (car q)) + (of (eq 'oldest-first (plist-get q :sort-order))) + (query (plist-get q :query))) + (notmuch-tree query nil nil nil nil nil nil of t)) + (let ((notmuch-saved-searches (jao-notmuch--index-searches searches))) + (notmuch-jump-search))) + (jao-notmuch-jump-search t))) + +;;; tags +(defvar jao-notmuch--shared-tags + '("new" "unread" "flagged" "signed" "sent" "attachment" "forwarded" "inbox" + "encrypted" "gmane" "gnus" "feeds" "rss" "mce" "trove" "prog" "emacs")) + +(setq notmuch-archive-tags + '("+trove" "-hacking" "-new" "-drivel" "-words" "-inbox") + notmuch-show-mark-read-tags '("-new" "-unread") + notmuch-tag-formats + (let (;; (d `(:foreground ,(face-attribute 'jao-themes-dimm :foreground))) + (e `(:foreground ,(face-attribute 'jao-themes-error :foreground)))) + `(("attachment" "⧺") + ("bigml" "b") + ("deleted" "⛒" (propertize tag 'face '(:underline nil ,@e))) + ("feeds" "f") + ("flagged" "✓") + ("forwarded" "→") + ("gmane" "g") + ("jao" "j") + ("lists" "l") + ("new" "·") + ("replied" "↩" (propertize tag 'face '(:family "Fira Code"))) + ("sent" "↑") + ("signed") + ("unread"))) + notmuch-tag-deleted-formats + '(("unread") + ("new") + ("flagged") + ("deleted") + (".*" (notmuch-apply-face tag 'notmuch-tag-deleted)))) + +(with-eval-after-load "notmuch-tag" + (advice-add #'notmuch-read-tag-changes + :filter-return (lambda (x) (mapcar #'string-trim x)))) + +;;; package +;; (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/") +(jao-load-path "notmuch") + +(use-package notmuch + :init + (setq notmuch-address-use-company t + notmuch-address-command 'as-is + notmuch-always-prompt-for-sender t + notmuch-draft-folder "drafts" + notmuch-draft-quoted-tags '("part") + notmuch-fcc-dirs '((".*" . "sent -new -unread +sent +jao")) + notmuch-maildir-use-notmuch-insert t) + + :custom ((notmuch-address-internal-completion '(sent nil))) + + :config + + (add-hook 'message-send-hook #'notmuch-mua-attachment-check) + + (when jao-notmuch-enabled + (define-key message-mode-map (kbd "C-c C-d") #'notmuch-draft-postpone) + (setq message-directory (file-name-as-directory jao-maildir) + message-auto-save-directory "/tmp" + mail-user-agent 'message-user-agent)) + + :bind (:map notmuch-common-keymap + (("E" . jao-notmuch-open-enclosure) + ("B" . notmuch-show-resend-message) + ("b" . jao-notmuch-browse-urls)))) + +(use-package jao-notmuch :demand t) + +;;; hello +(defun jao-notmuch-refresh-agenda () + (interactive) + (save-window-excursion (calendar) (jao-org-agenda))) + +(defun jao-notmuch-hello-first () + (interactive) + (let ((inhibit-message t)) + (goto-char (point-min)) + (widget-forward 1))) + +(defun jao-notmuch-refresh-hello (&optional agenda) + (interactive "P") + (ignore-errors + (when (and (string= "mail" (jao-afio-frame-name)) + (derived-mode-p 'notmuch-hello-mode)) + (when (not (string-blank-p jao-notmuch-minibuffer-string)) + (let ((notmuch-hello-auto-refresh nil)) (notmuch-hello))) + (let ((jao-minibuffer-inhibit t)) + (when agenda (jao-notmuch-refresh-agenda))) + (unless (widget-at) (jao-notmuch-hello-first)) + (jao-minibuffer-refresh)))) + +(defvar jao-notmuch-hello--sec-rx "^\\(\\[ [0-9]+\\|All tags:.+\\)") + +(defun jao-notmuch-hello-next-section () + (interactive) + (when (re-search-forward jao-notmuch-hello--sec-rx nil t) + (widget-forward 1))) + +(defun jao-notmuch-hello-prev-section () + (interactive) + (beginning-of-line) + (unless (looking-at-p jao-notmuch-hello--sec-rx) + (re-search-backward jao-notmuch-hello--sec-rx nil t)) + (when (re-search-backward jao-notmuch-hello--sec-rx nil t) + (end-of-line) + (widget-forward 1))) + +(defun jao-notmuch-hello-next () + (interactive) + (if (widget-at) + (widget-button-press (point)) + (jao-notmuch-hello-next-section))) + +(use-package notmuch-hello + :init + (setq notmuch-column-control 1.0 + notmuch-hello-hide-tags nil + notmuch-hello-thousands-separator "," + notmuch-hello-auto-refresh t + notmuch-show-all-tags-list nil + notmuch-show-logo nil + notmuch-show-empty-saved-searches nil) + + (add-to-list 'display-buffer-alist + '("\\*notmuch-hello\\*" + (display-buffer-reuse-window) + (body-function . (lambda (w) (set-window-margins w 1))))) + + :hook ((notmuch-hello-refresh . jao-notmuch-notify)) + + :config + (when jao-notmuch-enabled + (add-hook 'jao-afio-switch-hook #'jao-notmuch-refresh-hello)) + + :bind (("s-j" . jao-notmuch-jump) + :map notmuch-hello-mode-map + (("a" . jao-notmuch-refresh-agenda) + ("A" . jao-afio-open-mail) + ("g" . jao-notmuch-refresh-hello) + ("j" . jao-notmuch-jump-search) + ("n" . jao-notmuch-hello-next) + ("p" . widget-backward) + ("SPC" . widget-button-press) + ("/" . consult-notmuch) + ("." . jao-notmuch-hello-first) + ("[" . jao-notmuch-hello-prev-section) + ("]" . jao-notmuch-hello-next-section)))) + +;;; show +(defun jao-notmuch-open-enclosure (add) + (interactive "P") + (with-current-notmuch-show-message + (goto-char (point-min)) + (if (not (search-forward "Enclosure:" nil t)) + (user-error "No enclosure in message body") + (re-search-forward "https?://" nil t) + (if-let (url (thing-at-point-url-at-point)) + (progn + (message "%s %s ..." (if add "Adding" "Playing") url) + (unless add (jao-mpc-clear)) + (jao-mpc-add-url url) + (unless add (jao-mpc-play))) + (error "Found an enclosure, but not a link!"))))) + +(defconst jao-mail-clean-rx + (regexp-opt '("ElDiario.es - ElDiario.es: " + "The Guardian: " + "The Conversation – Articles (UK): " + "gr-qc updates on arXiv.org: " + "quant-ph updates on arXiv.org: " + " via The Rust Programming Language Forum"))) + +(defun jao-mail-clean-address (args) + (when-let ((address (car args))) + (list (thread-last (replace-regexp-in-string jao-mail-clean-rx "" address) + (replace-regexp-in-string " " ", "))))) + +(defun jao-notmuch-open-in-mac-mail () + (interactive) + (jao-d-l + (when-let* ((id (ignore-errors (notmuch-show-get-message-id)))) + (jao-mac-open "message://%%3C%s%%3E" (substring id 3))) + (message "Only available in macs!"))) + +(use-package notmuch-show + :init + (setq gnus-blocked-images "." + notmuch-message-headers + '("Subject" "To" "Cc" "Date" "Reply-To" "List-Id" "X-RSS-Feed") + notmuch-show-only-matching-messages t + notmuch-show-part-button-default-action 'notmuch-show-view-part + notmuch-wash-signature-lines-max 0 + notmuch-wash-wrap-lines-length 120 + notmuch-wash-citation-lines-prefix 120 + notmuch-wash-citation-lines-suffix 120 + notmuch-show-text/html-blocked-images "." + notmuch-show-header-line nil ;; #'jao-notmuch-message-header-line + jao-notmuch-header-line-format "[%N / %M / %T] %n / %m / %t") + + :config + + (advice-add 'notmuch-clean-address :filter-args #'jao-mail-clean-address) + (add-hook 'notmuch-show-mode-hook (lambda () (setq fill-column 80))) + + :bind + (:map notmuch-show-mode-map + (("h" . jao-notmuch-goto-tree-buffer) + ("r" . notmuch-show-reply) + ("R" . notmuch-show-reply-sender) + ("TAB" . jao-notmuch-show-next-button) + ([backtab] . jao-notmuch-show-previous-button) + ("RET" . jao-notmuch-show-ret) + ("M" . jao-notmuch-open-in-mac-mail)))) + +;;; search +(use-package notmuch-search + :init (setq notmuch-search-result-format + '(("date" . "%12s ") + ("count" . "%-7s ") + ("authors" . "%-35s") + ("subject" . " %-100s") + (jao-notmuch-format-tags . " (%s)")) + notmuch-search-buffer-name-format "*%s*" + notmuch-saved-search-buffer-name-format "*%s*") + :bind (:map notmuch-search-mode-map + (("RET" . notmuch-tree-from-search-thread) + ("M-RET" . notmuch-search-show-thread)))) + +;;; tree +(defun jao-notmuch-tree--forward (&optional prev) + (interactive) + (forward-line (if prev -1 1)) + (when prev (forward-char 2)) + (jao-notmuch-tree-scroll-or-next)) + +(defun jao-notmuch-tree--backward () + (interactive) + (jao-notmuch-tree--forward t)) + +(defun jao-notmuch--via-url () + (when (window-live-p notmuch-tree-message-window) + (with-selected-window notmuch-tree-message-window + (jao-url-email-url)))) + +(defun jao-notmuch-browse-url (ext) + (interactive "P") + (when-let* ((url (or (jao-notmuch--via-url) + (car (last (jao-notmuch-message-urls)))))) + (if ext + (funcall browse-url-secondary-browser-function url) + (window-configuration-to-register ?G) + (select-window notmuch-tree-message-window)t + (eww url)))) + +(defun jao-notmuch-from-eww (keep-eww-buffer) + (interactive "P") + (unless keep-eww-buffer (jao-eww-close)) + (jump-to-register ?G)) + +(with-eval-after-load 'eww + (define-key eww-mode-map (kbd "h") #'jao-notmuch-from-eww)) + +(defun jao-notmuch-adjust-tree-fonts (&optional family) + (let ((fg (face-attribute 'jao-themes-dimm :foreground))) + (dolist (f '(notmuch-tree-match-tree-face + notmuch-tree-no-match-tree-face)) + (if family + (set-face-attribute f nil :family family :foreground fg) + (set-face-attribute f nil :foreground fg))))) + +(use-package notmuch-tree + :init + (setq notmuch-tree-result-format + `(("date" . "%12s ") + ("authors" . "%-25s") + ;; (jao-notmuch-format-author . 25) + (jao-notmuch-format-msg-ticks . ,jao-mails-regexp) + (jao-notmuch-format-tree-and-subject . "%>-85s") + (jao-notmuch-format-tags . " (%s)")) + notmuch-unthreaded-result-format notmuch-search-result-format + consult-notmuch-result-format + `((jao-notmuch-format-msg-ticks . ,jao-mails-regexp) + ("date" . "%12s ") + ("authors" . "%-35s") + ("subject" . " %-100s") + (jao-notmuch-format-tags . " (%s)")) + notmuch-tree-thread-symbols + '((prefix . "─") (top . "─") (top-tee . "┬") + (vertical . "│") (vertical-tee . "├") (bottom . "╰") + (arrow . "")) + ;; notmuch-tree-thread-symbols + ;; '((prefix . " ") (top . " ") (top-tee . " ") + ;; (vertical . " ") (vertical-tee . " ") (bottom . " ") + ;; (arrow . "")) + notmuch-tree-outline-enabled t + notmuch-tree-outline-visibility 'hide-others + notmuch-tree-outline-auto-close t + notmuch-tree-outline-open-on-next t) + :config + + (when (display-graphic-p) + (jao-notmuch-adjust-tree-fonts + (when (string-prefix-p "Hack" jao-themes-default-face) "Source Code Pro"))) + + (defun jao-notmuch-before-tree (&rest _args) + (window-configuration-to-register ?G) + (when (string= (buffer-name) "*notmuch-hello*") + (when (< (frame-width) 230) (delete-other-windows)) + (split-window-right 40) + (other-window 1))) + + (defun jao-notmuch-after-tree-quit (&optional _both) + (when (not (derived-mode-p 'notmuch-tree-mode 'notmuch-hello-mode)) + (jump-to-register ?G) + (when (not (derived-mode-p 'notmuch-hello-mode)) + (jao-notmuch-notify-and-update)))) + + (defvar jao-notmuch--visits 0) + + (defun jao-notmuch--maybe-notify-and-update (&optional _both) + (when (not (derived-mode-p 'notmuch-hello-mode 'notmuch-tree-mode)) + (jao-notmuch-notify-and-update)) + (jao-notmuch-refresh-hello)) + + (defun jao-notmuch-tree--sentinel (proc) + (when (eq (process-status proc) 'exit) + (let ((inhibit-read-only t)) + (save-excursion + (goto-char (point-max)) + (when (re-search-backward "^End of search results." nil t) + (delete-line)))) + (jao-notmuch-thread-info-mode))) + + (add-hook 'notmuch-tree-process-exit-functions #'jao-notmuch-tree--sentinel) + (advice-add 'notmuch-tree-quit :after #'jao-notmuch--maybe-notify-and-update) + + (defun jao-notmuch-use-two-panes () + (interactive) + (advice-add 'notmuch-tree :before #'jao-notmuch-before-tree) + (advice-add 'notmuch-tree-quit :after #'jao-notmuch-after-tree-quit)) + + (defun jao-notmuch-use-one-pane () + (interactive) + (jao-afio-add-frame-parameters mail (width . 120)) + (advice-remove 'notmuch-tree #'jao-notmuch-before-tree) + (advice-remove 'notmuch-tree-quit #'jao-notmuch-after-tree-quit)) + + (when jao-notmuch-enabled + (jao-d-l (jao-notmuch-use-one-pane) (jao-notmuch-use-two-panes))) + + :bind (:map notmuch-tree-mode-map + (("b" . jao-notmuch-browse-urls) + ("d" . jao-notmuch-tree-toggle-delete) + ("D" . jao-notmuch-tree-toggle-delete-thread) + ("h" . jao-notmuch-goto-message-buffer) + ("i" . jao-notmuch-toggle-images) + ("k" . jao-notmuch-tree-read-thread) + ("K" . jao-notmuch-tree-mark-all-read) + ("N" . jao-notmuch-tree--forward) + ("M" . jao-notmuch-open-in-mac-mail) + ("O" . notmuch-tree-toggle-order) + ("o" . jao-notmuch-tree-widen-search) + ("P" . jao-notmuch-tree--backward) + ("r" . notmuch-tree-reply) + ("R" . notmuch-tree-reply-sender) + ("s" . jao-notmuch-tree-toggle-spam) + ("u" . jao-notmuch-tree-toggle-flag) + ("v" . notmuch-tree-scroll-message-window) + ("V" . notmuch-tree-scroll-message-window-back) + ("x" . jao-notmuch-arXiv-capture) + ("<" . jao-notmuch-tree-beginning-of-buffer) + (">" . jao-notmuch-tree-end-of-buffer) + ("\\" . notmuch-tree-view-raw-message) + ("." . jao-notmuch-toggle-mime-parts) + (";" . bbdb-mua-display-sender) + ("=" . jao-notmuch-tree-toggle-message) + ("RET" . jao-notmuch-tree-show-or-scroll) + ("SPC" . jao-notmuch-tree-scroll-or-next) + ("M-g" . jao-notmuch-browse-url) + ("M-u" . jao-notmuch-tree-reset-tags)))) + +;;; browse-url +(defvar jao-notmuch-url-rx "^notmuch:\\(/+\\|id:\\)\\(.+\\)") + +(defun jao-notmuch-open-url (url &rest _) + (and (string-match jao-notmuch-url-rx url) + (notmuch-show (concat "id:" (match-string 2 url))))) + +(add-to-list 'browse-url-handlers + (cons jao-notmuch-url-rx 'jao-notmuch-open-url)) + +;;; org mode +(defvar jao-org-notmuch-last-subject nil) +(defun jao-org-notmuch-last-subject () jao-org-notmuch-last-subject) + +(defun jao-notmuch--add-tags (tags) + (if (derived-mode-p 'notmuch-show-mode) + (notmuch-show-add-tag tags) + (notmuch-tree-add-tag tags))) + +(defun org-notmuch-store-link () + "Store a link to a notmuch mail message." + (cl-case major-mode + ((notmuch-show-mode notmuch-tree-mode) + ;; Store link to the current message + (let* ((id (notmuch-show-get-message-id)) + (link (concat "notmuch:" id)) + (subj (notmuch-show-get-subject)) + (description (format "Mail: %s" subj))) + (setq jao-org-notmuch-last-subject subj) + (when (y-or-n-p "Archive message? ") + (jao-notmuch--add-tags '("+trove"))) + (when (y-or-n-p "Flag message? ") + (jao-notmuch--add-tags '("+flagged"))) + (org-link-store-props :type "notmuch" + :link link + :description description))) + (notmuch-search-mode + ;; Store link to the thread on the current line + (let* ((id (notmuch-search-find-thread-id)) + (link (concat "notmuch:" id)) + (subj (notmuch-search-find-subject)) + (description (format "Mail: %s" subj))) + (setq jao-org-notmuch-last-subject subj) + (org-link-store-props + :type "notmuch" + :link link + :description description))))) + +(with-eval-after-load "org" + (org-link-set-parameters "notmuch" + :follow 'notmuch-show + :store 'org-notmuch-store-link)) +;;; arXiv +(use-package org-capture + :config + (when jao-notmuch-enabled + (add-to-list 'org-capture-templates + '("X" "arXiv" entry (file "notes/physics/arxiv.org") + "* %(jao-org-notmuch-last-subject)\n %i" + :immediate-finish t) + t) + (org-capture-upgrade-templates org-capture-templates))) + +(defun jao-notmuch-arXiv-capture () + (interactive) + (save-window-excursion + (jao-notmuch-goto-message-buffer) + (save-excursion + (goto-char (point-min)) + (re-search-forward "\\[ text/html \\]") + (forward-paragraph) + (setq-local transient-mark-mode 'lambda) + (set-mark (point)) + (goto-char (point-max)) + (org-capture nil "X")))) + +;;; html renderer + +;; we can use the default defined in jao-custom-ewww +;; (when jao-notmuch-enabled (setq mm-text-html-renderer 'shr)) + +;;; consult +(jao-when-linux + (jao-load-path "consult-notmuch") + (require 'consult-notmuch) + (setq consult-notmuch-newest-first t) + (consult-customize consult-notmuch :preview-key 'any) + + (defvar jao-consult-notmuch-history nil) + + (defvar jao-mailbox-folders '("bigml" "jao")) + + (defun jao-consult-notmuch-folder (&optional tree folder) + (interactive "P") + (let* ((folder (if folder + (file-name-as-directory folder) + (completing-read "Group: " + 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))) +;;; org +(when jao-notmuch-enabled (jao-notmuch-org-links)) + +;;; recoll +(jao-when-linux + (defun jao-notmuch-open-file (fname &optional _page) + (with-temp-buffer + (insert-file-contents-literally fname) + (goto-char (point-min)) + (and (re-search-forward "^Message-ID: <\\([^>]+\\)>$" nil t) + (notmuch-show (concat "id:" (match-string 1)))))) + + (when jao-notmuch-enabled + (with-eval-after-load "org" + (org-link-set-parameters "message" :follow #'jao-notmuch-open-file)) + + (with-eval-after-load "consult-recoll" + (add-to-list 'consult-recoll-open-fns + '("message/rfc822" . jao-notmuch-open-file))))) + +;;; . +(provide 'jao-custom-notmuch) |
