diff --git a/modules/app/notmuch/autoload.el b/modules/app/notmuch/autoload.el new file mode 100644 index 000000000..afccecd52 --- /dev/null +++ b/modules/app/notmuch/autoload.el @@ -0,0 +1,183 @@ +;;; app/notmuch/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun =notmuch () + "Activate (or switch to) `notmuch' in its workspace." + (interactive) + (unless (featurep! :feature workspaces) + (user-error ":feature workspaces is required, but disabled")) + (condition-case-unless-debug e + (progn + (+workspace-switch "*MAIL*" t) + (if-let* ((buf (cl-find-if (lambda (it) (string-match-p "^\\*notmuch" (buffer-name (window-buffer it)))) + (doom-visible-windows)))) + (select-window (get-buffer-window buf)) + (notmuch-search "tag:inbox")) + (+workspace/display)) + ('error + (+notmuch/quit) + (signal (car e) (cdr e))))) + + +;; +;; Commands +;; + +;;;###autoload +(defun +notmuch/quit () + (interactive) + ;; (+popup/close (get-buffer-window "*notmuch-hello*")) + (doom-kill-matching-buffers "^\\*notmuch") + (+workspace/delete "*MAIL*")) + +;;;###autoload +(defun +notmuch/update () + (interactive) + (start-process-shell-command + "notmuch update" nil + "cd ~/.mail/account.gmail && /usr/local/bin/gmi push && /usr/local/bin/gmi pull && /usr/local/bin/notmuch new && /usr/local/bin/afew -a -t")) + +;;;###autoload +(defun +notmuch/search-delete () + (interactive) + (notmuch-search-add-tag (list "+trash" "-inbox" "-unread")) + (notmuch-tree-next-message)) + +;;;###autoload +(defun +notmuch/tree-delete () + (interactive) + (notmuch-tree-add-tag (list "+trash" "-inbox" "-unread")) + (notmuch-tree-next-message)) + +;;;###autoload +(defun +notmuch/search-spam () + (interactive) + (notmuch-search-add-tag (list "+spam" "-inbox" "-unread")) + (notmuch-search-next-thread)) + +;;;###autoload +(defun +notmuch/tree-spam () + (interactive) + (notmuch-tree-add-tag (list "+spam" "-inbox" "-unread")) + (notmuch-tree-next-message)) + +;;;###autoload +(defun +notmuch/open-message-with-mail-app-notmuch-tree () + (interactive) + (let* ((msg-path (car (plist-get (notmuch-tree-get-message-properties) :filename))) + (temp (make-temp-file "notmuch-message-" nil ".eml"))) + (shell-command-to-string (format "cp '%s' '%s'" msg-path temp)) + (start-process-shell-command "email" nil (format "xdg-open '%s'" temp)))) + +;;;###autoload +(defun +notmuch/open-message-with-mail-app-notmuch-show () + (interactive) + (let* ((msg-path (car (plist-get (notmuch-show-get-message-properties) :filename))) + (temp (make-temp-file "notmuch-message-" nil ".eml"))) + (shell-command-to-string (format "cp '%s' '%s'" msg-path temp)) + (start-process-shell-command "email" nil (format "xdg-open '%s'" temp)))) + + +;; +;; Advice +;; + +;;;###autoload +(defun +notmuch*dont-confirm-on-kill-process (orig-fn &rest args) + "Don't prompt for confirmation when killing notmuch sentinel." + (let (confirm-kill-processes) + (apply orig-fn args))) + +;; (defun +notmuch*hello-insert-searches (title query-list &rest options) +;; (widget-insert (propertize title 'face 'org-agenda-structure)) +;; (if (and notmuch-hello-first-run (plist-get options :initially-hidden)) +;; (add-to-list 'notmuch-hello-hidden-sections title)) +;; (let ((is-hidden (member title notmuch-hello-hidden-sections)) +;; (widget-push-button-prefix "") +;; (widget-push-button-suffix "") +;; (start (point))) +;; (if is-hidden +;; (widget-create 'push-button +;; :notify `(lambda (widget &rest ignore) +;; (setq notmuch-hello-hidden-sections +;; (delete ,title notmuch-hello-hidden-sections)) +;; (notmuch-hello-update)) +;; (propertize " +" 'face 'org-agenda-structure)) +;; (widget-create 'push-button +;; :notify `(lambda (widget &rest ignore) +;; (add-to-list 'notmuch-hello-hidden-sections +;; ,title) +;; (notmuch-hello-update)) +;; " -")) +;; (widget-insert "\n") +;; (when (not is-hidden) +;; (let ((searches (apply 'notmuch-hello-query-counts query-list options))) +;; (when (or (not (plist-get options :hide-if-empty)) +;; searches) +;; (widget-insert "\n") +;; (notmuch-hello-insert-buttons searches) +;; (indent-rigidly start (point) notmuch-hello-indent)))))) + +;; (defun +notmuch*hello-insert-saved-searches () +;; "Insert the saved-searches section." +;; (let ((searches (notmuch-hello-query-counts +;; (if notmuch-saved-search-sort-function +;; (funcall notmuch-saved-search-sort-function +;; notmuch-saved-searches) +;; notmuch-saved-searches) +;; :show-empty-searches notmuch-show-empty-saved-searches))) +;; (when searches +;; (widget-insert (propertize "Notmuch" 'face 'org-agenda-date-today)) +;; (widget-insert "\n\n") +;; (widget-insert (propertize "Saved searches" 'face 'org-agenda-structure)) +;; (widget-insert "\n\n") +;; (let ((start (point))) +;; (notmuch-hello-insert-buttons searches) +;; (indent-rigidly start (point) notmuch-hello-indent))))) + +;; (defun +notmuch*hello-insert-buttons (searches) +;; (let* ((widest (notmuch-hello-longest-label searches)) +;; (tags-and-width (notmuch-hello-tags-per-line widest)) +;; (tags-per-line (car tags-and-width)) +;; (column-width (cdr tags-and-width)) +;; (column-indent 0) +;; (count 0) +;; (reordered-list (notmuch-hello-reflect searches tags-per-line)) +;; ;; Hack the display of the buttons used. +;; (widget-push-button-prefix "") +;; (widget-push-button-suffix "")) +;; ;; dme: It feels as though there should be a better way to +;; ;; implement this loop than using an incrementing counter. +;; (mapc (lambda (elem) +;; ;; (not elem) indicates an empty slot in the matrix. +;; (when elem +;; (if (> column-indent 0) +;; (widget-insert (make-string column-indent ? ))) +;; (let* ((name (plist-get elem :name)) +;; (query (plist-get elem :query)) +;; (oldest-first (case (plist-get elem :sort-order) +;; (newest-first nil) +;; (oldest-first t) +;; (otherwise notmuch-search-oldest-first))) +;; (search-type (eq (plist-get elem :search-type) 'tree)) +;; (msg-count (plist-get elem :count))) +;; (widget-insert (format "\n%5s " +;; (notmuch-hello-nice-number msg-count))) +;; (widget-create 'push-button +;; :notify #'notmuch-hello-widget-search +;; :notmuch-search-terms query +;; :notmuch-search-oldest-first oldest-first +;; :notmuch-search-type search-type +;; name) +;; (setq column-indent +;; (1+ (max 0 (- column-width (length name))))))) +;; (setq count (1+ count)) +;; (when (eq (% count tags-per-line) 0) +;; (setq column-indent 0) +;; (widget-insert "\n"))) +;; reordered-list) + +;; ;; If the last line was not full (and hence did not include a +;; ;; carriage return), insert one now. +;; (unless (eq (% count tags-per-line) 0) +;; (widget-insert "\n")))) diff --git a/modules/app/notmuch/config.el b/modules/app/notmuch/config.el new file mode 100644 index 000000000..f415211e7 --- /dev/null +++ b/modules/app/notmuch/config.el @@ -0,0 +1,68 @@ +;;; app/notmuch/config.el -*- lexical-binding: t; -*- + +;; FIXME This module is a WIP! + +(after! notmuch + (set-company-backend! 'notmuch-message-mode + '(notmuch-company (company-ispell :with company-yasnippet))) + + (set-popup-rule! "^\\*notmuch-hello" :side 'left :size 30 :ttl 0) + + (setq notmuch-fcc-dirs nil + notmuch-show-logo nil + notmuch-message-headers-visible nil + message-kill-buffer-on-exit t + message-send-mail-function 'message-send-mail-with-sendmail + notmuch-search-oldest-first nil + send-mail-function 'sendmail-send-it + ;; sendmail-program "/usr/local/bin/msmtp" + notmuch-search-result-format + '(("date" . "%12s ") + ("count" . "%-7s ") + ("authors" . "%-30s ") + ("subject" . "%-72s ") + ("tags" . "(%s)")) + notmuch-tag-formats + '(("unread" (propertize tag 'face 'notmuch-tag-unread))) + notmuch-hello-sections + '(notmuch-hello-insert-saved-searches + notmuch-hello-insert-alltags) + notmuch-saved-searches + '((:name "inbox" :query "tag:inbox not tag:trash" :key "i") + (:name "flagged" :query "tag:flagged" :key "f") + (:name "sent" :query "tag:sent" :key "s") + (:name "drafts" :query "tag:draft" :key "d")) + notmuch-archive-tags '("-inbox" "-unread")) + + ;; (setq-hook! 'notmuch-show-mode-hook line-spacing 0) + + (add-to-list 'doom-real-buffer-functions #'notmuch-interesting-buffer nil #'eq) + + (advice-add #'notmuch-start-notmuch-sentinel :around #'+notmuch*dont-confirm-on-kill-process) + + ;; Visual enhancements + (defun +notmuch|center-window () + (setq-local visual-fill-column-width 90) + (visual-fill-column-mode)) + (add-hook 'notmuch-show-mode-hook #'+notmuch|center-window) + + ;; modeline doesn't have much use in these modes + (add-hook! (notmuch-show-mode notmuch-tree-mode notmuch-search-mode) + #'hide-mode-line-mode)) + + +(def-package! org-mime + :after (org notmuch) + :config (setq org-mime-library 'mml)) + + +(def-package! counsel-notmuch + :when (featurep! :completion ivy) + :commands counsel-notmuch + :after notmuch) + +(def-package! helm-notmuch + :when (featurep! :completion helm) + :commands helm-notmuch + :after notmuch) + diff --git a/modules/app/notmuch/packages.el b/modules/app/notmuch/packages.el new file mode 100644 index 000000000..7caea8546 --- /dev/null +++ b/modules/app/notmuch/packages.el @@ -0,0 +1,9 @@ +;; -*- no-byte-compile: t; -*- +;;; app/notmuch/packages.el + +(package! notmuch) +(package! org-mime) +(when (featurep! :completion ivy) + (package! counsel-notmuch)) +(when (featurep! :completion helm) + (package! helm-notmuch))