summaryrefslogtreecommitdiffhomepage
path: root/gnus.org
diff options
context:
space:
mode:
Diffstat (limited to 'gnus.org')
-rw-r--r--gnus.org660
1 files changed, 660 insertions, 0 deletions
diff --git a/gnus.org b/gnus.org
new file mode 100644
index 0000000..6d87741
--- /dev/null
+++ b/gnus.org
@@ -0,0 +1,660 @@
+#+PROPERTY: header-args :tangle yes :comments yes :results silent
+
+* Feature switching vars
+ #+BEGIN_SRC emacs-lisp
+ (defvar jao-gnus-use-local-imap t)
+ (defvar jao-gnus-use-leafnode t)
+ (defvar jao-gnus-use-pm-imap nil)
+ (defvar jao-gnus-use-gmail nil)
+ (defvar jao-gnus-use-gmane nil)
+ (defvar jao-gnus-use-nnml nil)
+ (defvar jao-gnus-use-maildirs nil)
+ (defvar jao-gnus-use-nnreddit nil)
+#+END_SRC
+* Looks
+*** Verbosity
+ #+begin_src emacs-lisp
+ (setq gnus-verbose 5)
+ #+end_src
+*** Geometry
+ #+BEGIN_SRC emacs-lisp
+ ;;; geometry:
+ (defvar jao-gnus-use-three-panes t)
+
+ (setq gnus-use-trees nil
+ gnus-generate-tree-function 'gnus-generate-horizontal-tree
+ gnus-tree-minimize-window nil)
+
+ (when jao-gnus-use-three-panes
+ (let ((side-bar '(vertical 1.0
+ ("inbox.org" 0.4)
+ ("*Org Agenda*" 1.0)
+ ("*Calendar*" 8)))
+ (wide-len 190))
+ (gnus-add-configuration
+ `(article
+ (horizontal 1.0
+ (vertical 63 (group 1.0))
+ (vertical 127
+ (summary 0.25 point)
+ (article 1.0))
+ ,side-bar)))
+
+ (gnus-add-configuration
+ `(group (horizontal 1.0 (group ,wide-len point) ,side-bar)))
+
+ (gnus-add-configuration
+ `(message (horizontal 1.0 (message ,wide-len point) ,side-bar)))
+
+ (gnus-add-configuration
+ `(reply-yank (horizontal 1.0 (message ,wide-len point) ,side-bar)))
+
+ (gnus-add-configuration
+ `(summary
+ (horizontal 1.0
+ (vertical 63 (group 1.0))
+ (vertical 127 (summary 1.0 point))
+ ,side-bar)))
+
+ (gnus-add-configuration
+ `(reply
+ (horizontal 1.0
+ (message 90 point)
+ (article 100)
+ ,side-bar)))))
+
+ (defun jao-gnus--summary-done ()
+ (save-window-excursion (org-agenda-list)))
+
+ (add-hook 'gnus-summary-prepared-hook #'jao-gnus--summary-done)
+ #+END_SRC
+*** No blue icon
+ #+begin_src emacs-lisp
+ ;; (defalias 'gnus-mode-line-buffer-identification 'identity)
+ (advice-add 'gnus-mode-line-buffer-identification :override #'identity)
+ (setq gnus-mode-line-image-cache nil)
+ #+end_src
+* Search
+ [[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/"))
+
+ (defun jao-gnus-search-engine (engine)
+ `(gnus-search-engine ,engine (remove-prefix ,jao-gnus-search-prefix)))
+ #+end_src
+* News server
+ #+begin_src emacs-lisp
+ (setq gnus-select-method
+ (cond (jao-gnus-use-leafnode
+ `(nntp "localhost"
+ ,(jao-gnus-search-engine 'gnus-search-notmuch)))
+ (jao-gnus-use-gmane '(nntp "news.gmane.io"))
+ (t '(nnnil ""))))
+ (setq gnus-secondary-select-methods '())
+
+ (setq gnus-ignored-newsgroups
+ "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]]\"[#'()]")
+
+ ;; nntp options
+ (setq nnheader-read-timeout 0.02)
+ #+end_src
+* IMAP servers
+ #+begin_src emacs-lisp
+ ;; archiving messages
+ (setq gnus-message-archive-group nil)
+
+ ;; imap
+ (when jao-gnus-use-local-imap
+ (add-to-list 'gnus-secondary-select-methods
+ `(nnimap ""
+ (nnimap-expunge immediately)
+ (nnimap-address "localhost"))))
+
+ (when jao-gnus-use-pm-imap
+ (add-to-list 'gnus-secondary-select-methods
+ '(nnimap "pm"
+ (nnimap-address "127.0.0.1")
+ (nnimap-stream network)
+ (nnimap-server-port 1143))))
+
+ (when jao-gnus-use-gmail
+ (add-to-list 'gnus-secondary-select-methods
+ '(nnimap "gmail" (nnimap-address "imap.gmail.com")))
+ (add-to-list 'gnus-secondary-select-methods
+ '(nnimap "bigml" (nnimap-address "imap.gmail.com"))))
+ #+end_src
+* Mailbox and maildir servers
+ #+begin_src emacs-lisp
+ (setq mail-sources '((file :path "/var/mail/jao")))
+
+ (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 "/home/jao/var/maildir/bigml/")))
+ (add-to-list 'gnus-secondary-select-methods
+ '(nnmaildir "jao" (directory "/home/jao/var/maildir/jao/")))
+ (add-to-list 'gnus-secondary-select-methods
+ '(nnmaildir "gmail" (directory "/home/jao/var/maildir/gmail/"))))
+
+ #+end_src
+* RSS servers
+ #+begin_src emacs-lisp
+ (setq nnrss-use-local nil
+ nnrss-directory (jao-gnus-dir "rss"))
+ (setq nnrss-wash-html-in-text-plain-parts t)
+ (setq nnrss-ignore-article-fields '(description
+ comments
+ dc:date
+ slash:comments
+ slash:description))
+
+ ;; (use-package nnreddit
+ ;; :ensure t
+ ;; :bind (:map gnus-group-mode-map (("R R" . gnus-group-restart))))
+
+ ;; (when jao-gnus-use-nnreddit
+ ;; (add-to-list 'gnus-secondary-select-methods '(nnreddit "")))
+
+ #+end_src
+* Agents, demons, synchronicity
+ #+BEGIN_SRC emacs-lisp
+ ;; gnus agent(s) and demons
+ ;; (setq gnus-agent nil)
+ (setq mail-user-agent 'gnus-user-agent)
+ (require 'gnus-demon)
+ (when (featurep 'gnus-desktop-notify)
+ (gnus-desktop-notify-mode 1)
+ (gnus-demon-add-scanmail))
+
+ ;; synchronicity
+ (setq gnus-asynchronous t)
+ ;;; prefetch as many articles as possible
+ (setq gnus-use-article-prefetch nil)
+
+ (setq gnus-save-killed-list nil)
+ (setq gnus-check-new-newsgroups nil)
+
+ (setq gnus-gcc-mark-as-read t)
+ #+END_SRC
+* Delayed messages
+ #+BEGIN_SRC emacs-lisp
+ ;;; delayed messages (C-cC-j in message buffer)
+ (require 'gnus-util)
+ (gnus-delay-initialize)
+ (setq gnus-delay-default-delay "3h")
+ ;;; so that the Date is set when the message is sent, not when it's
+ ;;; delayed
+ (eval-after-load "message"
+ '(setq message-draft-headers (remove 'Date message-draft-headers)))
+ #+END_SRC
+* Groups buffer
+ #+BEGIN_SRC emacs-lisp
+ ;; (setq gnus-group-line-format " %m%S%p%P:%~(pad-right 35)c %3y %B\n")
+ (setq gnus-group-line-format " %m%S%p%3y%P%* %~(pad-right 45)G %B\n")
+ (setq gnus-topic-line-format "%i[ %(%{%n%}%) -- %A ]%v\n")
+ (setq gnus-group-uncollapsed-levels 2)
+ (setq gnus-auto-select-subject 'unread)
+ (setq-default gnus-large-newsgroup nil)
+
+ (add-hook 'gnus-select-group-hook 'gnus-group-set-timestamp)
+ (add-hook 'gnus-group-mode-hook 'gnus-topic-mode)
+
+ (defvar jao-gnus--expire-every 50)
+ (defvar jao-gnus--get-count (1+ jao-gnus--expire-every))
+
+ (defun jao-gnus-get-new-news (&optional arg)
+ (interactive "p")
+ (when (and jao-gnus--expire-every
+ (> jao-gnus--get-count jao-gnus--expire-every))
+ (when jao-gnus-use-pm-imap (gnus-group-catchup "nnimap:pm/spam" t))
+ (gnus-group-expire-all-groups)
+ (setq jao-gnus--get-count 0))
+ (setq jao-gnus--get-count (1+ jao-gnus--get-count))
+ (gnus-group-get-new-news (max (if (= 1 jao-gnus--get-count) 4 3)
+ (or arg 0))))
+
+ (defun jao-gnus-restart-servers ()
+ (interactive)
+ (message "Restarting all servers...")
+ (gnus-group-enter-server-mode)
+ (gnus-server-close-all-servers)
+ (gnus-server-open-all-servers)
+ (gnus-server-exit)
+ (message "Restarting all servers... done"))
+
+
+ ;; To limit expiration to the `g' count, `jao-gnus--get-count':
+ ;; (remove-hook 'gnus-summary-prepare-exit-hook 'gnus-summary-expire-articles)
+
+ (define-key gnus-group-mode-map "g" 'jao-gnus-get-new-news)
+ (define-key gnus-group-mode-map "Z" 'jao-gnus-restart-servers)
+ #+END_SRC
+* Group parameters
+ #+begin_src emacs-lisp
+ (setq jao-gnus-expirable
+ (format (concat "^nnimap:\\("
+ "\\(\\(bigml\\|bml\\)/%s\\)\\|"
+ "\\(\\(jao\\|pm\\)/%s\\)\\|"
+ "\\(feeds/.+\\)\\|trash"
+ "\\)")
+ (regexp-opt '("support" "reports" "deploys"
+ "lists" "drivel" "bugs"))
+ (regexp-opt '("books" "think" "local" "drivel"
+ "lists" "emacs" "lobsters"))))
+
+ (setq gnus-parameters
+ `(("^nnimap:jao/.*"
+ (jao-gnus--trash-group "nnimap:trash")
+ (jao-gnus--archiving-group "nnimap:trove/jao"))
+ ("^nnimap:pm/.*"
+ (jao-gnus--trash-group "nnimap:pm/trash")
+ (jao-gnus--spam-group "nnimap:pm/spam")
+ (jao-gnus--archiving-group "nnimap:pm/archive"))
+ ("^nnimap:\\(jao\\|pm\\)/\\(trash\\|spam\\)"
+ (gcc-self . nil)
+ (auto-expire . t)
+ (total-expire . t)
+ (expiry-wait . 0.1)
+ (jao-gnus--trash-group nil)
+ (expiry-target . delete))
+ ("^nnimap:jao/inbox"
+ (gcc-self . t)
+ (posting-style (gcc "nnimap:trove/jao")))
+ ("^nnimap:b\\(ig\\)?ml/inbox"
+ (gcc-self . t)
+ (auto-expire . t)
+ (total-expire . t)
+ (expiry-wait . 7.0)
+ (jao-gnus--trash-group "nnimap:trash")
+ (expiry-target . "nnimap:bigml/trove"))
+ ("^nnimap:bml/inbox"
+ (jao-gnus--trash-group "nnimap:bml/trash")
+ (expiry-target . "nnimap:bml/trove"))
+ ("^nnimap:b\\(ig\\)?ml/.*"
+ (posting-style (address "jao@bigml.com"))
+ (jao-gnus--archiving-group "nnimap:bml/trove"))
+ ("^nnimap:b\\(ig\\)?ml/support"
+ (posting-style (address "support@bigml.com")))
+ (,jao-gnus-expirable
+ (jao-gnus--trash-group nil)
+ (gcc-self . nil)
+ (auto-expire . t)
+ (total-expire . t)
+ (expiry-wait . 3)
+ (expiry-target . delete))
+ ("^nnimap:feeds/podcasts"
+ (auto-expire . nil)
+ (total-expire . nil))
+ ("^nnimap:feeds/fun"
+ ;; (mm-text-html-renderer 'shr)
+ (gnus-inhibit-images nil))
+ ("^nnimap:feeds/\\(papers\\|programming\\|math\\|physics\\)$"
+ (expiry-wait . 30)
+ (jao-gnus--archiving-group "nnimap:trove/tech")
+ (posting-style (address "jao@gnu.org")))
+ ("^nnimap:feeds/\\(physics\\|math\\|papers\\)$"
+ (jao-gnus--archiving-group "nnimap:trove/sci"))
+ ("\\(gmane\\|gwene\\)\\..*"
+ (jao-gnus--archiving-group "nnimap:trove/tech")
+ (posting-style (address "jao@gnu.org")))))
+ #+end_src
+* Summary buffer
+*** Configuration, summary line
+ #+BEGIN_SRC emacs-lisp
+ (setq gnus-summary-ignore-duplicates t
+ gnus-suppress-duplicates t
+ gnus-summary-ignored-from-addresses jao-mails-regexp)
+
+ (setq gnus-show-threads t
+ gnus-thread-hide-subtree t
+ gnus-summary-make-false-root 'adopt
+ gnus-summary-gather-subject-limit 120
+ gnus-sort-gathered-threads-function 'gnus-thread-sort-by-date
+ gnus-thread-sort-functions '(gnus-thread-sort-by-date))
+
+ (setq gnus-face-1 'jao-gnus-face-tree)
+
+ (setq gnus-not-empty-thread-mark ?·) ; ↓)
+ (setq jao-gnus--summary-line-fmt
+ (concat "%%U %%*%%R %%uj "
+ "[ %%~(max-right 20)~(pad-right 20)n "
+ " %%I%%~(pad-left 2)t ] %%s"
+ "%%-%s="
+ "%%~(max-right 8)~(pad-left 8)&user-date;"
+ "\n"))
+
+ (defun jao-gnus--set-summary-line ()
+ (let* ((d (if jao-gnus-use-three-panes 75 12))
+ (w (- (window-width) d)))
+ (setq gnus-summary-line-format (format jao-gnus--summary-line-fmt w))))
+
+ (add-hook 'gnus-group-prepare-hook 'jao-gnus--set-summary-line)
+
+ (add-to-list 'nnmail-extra-headers 'Cc)
+ (add-to-list 'nnmail-extra-headers 'BCc)
+ (add-to-list 'gnus-extra-headers 'Cc)
+ (add-to-list 'gnus-extra-headers 'BCc)
+
+ (defun gnus-user-format-function-j (headers)
+ (let ((to (gnus-extra-header 'To headers)))
+ (if (string-match jao-mails-regexp to)
+ (if (string-match "," to) "¬" "»") ;; "~" "=")
+ (if (or (string-match jao-mails-regexp
+ (gnus-extra-header 'Cc headers))
+ (string-match jao-mails-regexp
+ (gnus-extra-header 'BCc headers)))
+ "¬" ;; "~"
+ " "))))
+
+ (setq gnus-summary-user-date-format-alist
+ '(((gnus-seconds-today) . "%H:%M")
+ ((+ 86400 (gnus-seconds-today)) . "'%H:%M")
+ ;; (604800 . "%a %H:%M") ;;that's one week
+ ((gnus-seconds-month) . "%a %d")
+ ((gnus-seconds-year) . "%b %d")
+ (t . "%b '%y")))
+
+ ;; old name, for emacs 23
+ (setq gnus-user-date-format-alist gnus-summary-user-date-format-alist)
+ #+END_SRC
+*** Moving messages around
+ #+BEGIN_SRC emacs-lisp
+ (defvar-local jao-gnus--spam-group nil)
+ (defvar-local jao-gnus--archiving-group nil)
+ (defvar-local jao-gnus--archive-as-copy-p nil)
+
+ (defvar jao-gnus--last-move nil)
+ (defun jao-gnus-move-hook (a headers c to d)
+ (setq jao-gnus--last-move (cons to (mail-header-id headers))))
+ (defun jao-gnus-goto-last-moved ()
+ (interactive)
+ (when jao-gnus--last-move
+ (when (eq major-mode 'gnus-summary-mode) (gnus-summary-exit))
+ (gnus-group-goto-group (car jao-gnus--last-move))
+ (gnus-group-select-group)
+ (gnus-summary-goto-article (cdr jao-gnus--last-move) nil t)))
+ (add-hook 'gnus-summary-article-move-hook 'jao-gnus-move-hook)
+
+ (defun jao-gnus-archive (follow)
+ (interactive "P")
+ (if jao-gnus--archiving-group
+ (progn
+ (if (or jao-gnus--archive-as-copy-p
+ (not (gnus-check-backend-function
+ 'request-move-article gnus-newsgroup-name)))
+ (gnus-summary-copy-article nil jao-gnus--archiving-group)
+ (gnus-summary-move-article nil jao-gnus--archiving-group))
+ (when follow (jao-gnus-goto-last-moved)))
+ (gnus-summary-mark-as-read)
+ (gnus-summary-delete-article)))
+
+ (defun jao-gnus-archive-tickingly ()
+ (interactive)
+ (gnus-summary-tick-article)
+ (jao-gnus-archive)
+ (when jao-gnus--archive-as-copy-p
+ (gnus-summary-mark-as-read)))
+
+ (defun jao-gnus-show-tickled ()
+ (interactive)
+ (gnus-summary-limit-to-marks "!"))
+
+ (make-variable-buffer-local
+ (defvar jao-gnus--trash-group nil))
+
+ (defun jao-gnus-trash ()
+ (interactive)
+ (gnus-summary-mark-as-read)
+ (if jao-gnus--trash-group
+ (gnus-summary-move-article nil jao-gnus--trash-group)
+ (gnus-summary-delete-article)))
+
+ (defun jao-gnus-move-to-spam ()
+ (interactive)
+ (gnus-summary-mark-as-read)
+ (gnus-summary-move-article nil jao-gnus--spam-group))
+
+ (define-key gnus-summary-mode-map "Ba" 'jao-gnus-archive)
+ (define-key gnus-summary-mode-map "BA" 'jao-gnus-archive-tickingly)
+ (define-key gnus-summary-mode-map "Bl" 'jao-gnus-goto-last-moved)
+
+ (define-key gnus-summary-mode-map (kbd "B DEL") 'jao-gnus-trash)
+ (define-key gnus-summary-mode-map (kbd "B <backspace>") 'jao-gnus-trash)
+ (define-key gnus-summary-mode-map "Bs" 'jao-gnus-move-to-spam)
+ (define-key gnus-summary-mode-map "/!" 'jao-gnus-show-tickled)
+ (define-key gnus-summary-mode-map [f7] 'gnus-summary-force-verify-and-decrypt)
+ #+END_SRC
+*** Writing emails
+ #+BEGIN_SRC emacs-lisp
+ (setq gnus-default-article-saver 'gnus-summary-save-article-mail)
+ (defvar jao-gnus-file-save-directory (expand-file-name "~/tmp"))
+ (defun jao-gnus-file-save (newsgroup headers &optional last-file)
+ (expand-file-name (format "%s.eml" (mail-header-subject headers))
+ jao-gnus-file-save-directory))
+ (setq gnus-mail-save-name 'jao-gnus-file-save)
+ #+END_SRC
+*** arXiv capture
+ #+begin_src emacs-lisp
+ (use-package org-capture
+ :config
+ (add-to-list 'org-capture-templates
+ '("X" "arXiv" entry (file+olp "misc.org" "Physics" "arXiv")
+ "* %:subject\n %i" :immediate-finish t)
+ t)
+ (org-capture-upgrade-templates org-capture-templates))
+
+ (defun jao-gnus-arXiv-capture ()
+ (interactive)
+ (gnus-summary-select-article-buffer)
+ (gnus-article-goto-part 0)
+ (forward-paragraph)
+ (setq-local transient-mark-mode 'lambda)
+ (set-mark (point))
+ (goto-char (point-max))
+ (org-capture nil "X"))
+ #+end_src
+* Article buffer
+*** Config, headers
+ #+BEGIN_SRC emacs-lisp
+ (setq mail-source-delete-incoming t)
+ (setq gnus-treat-display-smileys nil)
+ (setq gnus-treat-fill-long-lines nil)
+ (setq gnus-treat-fill-article nil)
+ (setq gnus-article-auto-eval-lisp-snippets nil)
+ (setq gnus-posting-styles '((".*" (name "Jose A. Ortega Ruiz"))))
+ (setq gnus-single-article-buffer nil)
+ (setq gnus-article-update-lapsed-header 60)
+ (setq gnus-article-update-date-headers 60)
+
+ (eval-after-load "gnus-art"
+ '(setq
+ gnus-visible-headers
+ (concat
+ gnus-visible-headers
+ "\\|^X-Newsreader:\\|^X-Mailer:\\|User-Agent:\\|X-User-Agent:")))
+ #+END_SRC
+*** HTML email
+ #+BEGIN_SRC emacs-lisp
+ ;; use w3m for html mail
+ (defun jao-gnus-html-renderer (handle)
+ (let ((w3m-message-silent t))
+ (condition-case nil
+ (mm-inline-text-html-render-with-w3m handle)
+ (error (delete-region (point) (point-max))
+ (mm-shr handle)))))
+
+ (setq gnus-button-url 'browse-url-generic
+ gnus-inhibit-images t
+ mm-text-html-renderer 'jao-gnus-html-renderer ;; 'w3m ;; 'shr
+ shr-use-colors nil
+ shr-use-fonts nil
+ mm-w3m-safe-url-regexp nil
+ mm-discouraged-alternatives nil ;; '("text/html" "text/richtext")
+ mm-inline-large-images 'resize)
+
+ ;; no html in From: (washing articles from arxiv feeds)
+ (require 'shr)
+ (defun jao-gnus-remove-anchors ()
+ (save-excursion
+ (goto-char (point-min))
+ (when (re-search-forward "updates on arXiv.org: <a" nil t)
+ (let ((begin (- (point) 3)))
+ (when (re-search-forward "^\\(To\\|Subject\\):" nil t)
+ (beginning-of-line)
+ (let ((shr-width 1000))
+ (shr-render-region begin (- (point) 1))
+ (goto-char begin)
+ (insert " ")))))))
+
+ (add-hook 'gnus-part-display-hook 'jao-gnus-remove-anchors)
+
+ ;; show images
+ (defun jao-gnus-show-image (&optional external)
+ (interactive "P")
+ (when (eq major-mode 'gnus-summary-mode)
+ (gnus-summary-select-article-buffer))
+ (let ((pos (next-single-property-change (point) 'w3m-image)))
+ (if (not pos)
+ (gnus-article-show-images)
+ (goto-char pos)
+ (if external (w3m-view-image) (w3m-toggle-inline-image)))))
+
+ (defun jao-gnus-show-images (&optional external)
+ (interactive "P")
+ (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))))))
+ #+END_SRC
+*** Follow links and enclosures
+ #+begin_src emacs-lisp
+ (defun jao-gnus-follow-link (&optional external)
+ (interactive "P")
+ (when (eq major-mode 'gnus-summary-mode)
+ (gnus-summary-select-article-buffer))
+ (save-excursion
+ (goto-char (point-min))
+ (when (or (search-forward-regexp "^Via: h" nil t)
+ (search-forward-regexp "^URL: h" nil t)
+ (and (search-forward-regexp "^Link$" nil t)
+ (not (beginning-of-line))))
+ (if external
+ (jao-browse-with-external-browser)
+ (w3m-safe-view-this-url)))))
+
+ (defun jao-gnus-open-enclosure (&optional playp)
+ (interactive "P")
+ (gnus-summary-select-article-buffer)
+ (save-excursion
+ (goto-char (point-min))
+ (when (search-forward "Enclosure:")
+ (forward-char 2)
+ (when-let ((url (thing-at-point-url-at-point)))
+ (message "%s %s ..." (if playp "Playing" "Adding") url)
+ (if playp (emms-play-url url) (emms-add-url url))
+ (when playp
+ (sit-for 1)
+ (jao-emms-echo))))))
+ #+end_src
+*** Tweets and toots
+ #+BEGIN_SRC emacs-lisp
+ (defun jao-gnus--find-link ()
+ (when (eq major-mode 'gnus-summary-mode)
+ (gnus-summary-select-article-buffer))
+ (goto-char (point-max))
+ (or (search-backward-regexp "^Link\\b" nil t)
+ (search-backward-regexp "^URL: h" nil t)
+ (search-backward-regexp "^Via: h"))
+ (goto-char (match-end 0))
+ (jao-w3m--toot-text nil nil ""))
+
+ (defun jao-gnus-tweet-link (&optional toot)
+ (interactive "P")
+ (save-excursion
+ (jao-gnus--find-link)
+ (if toot
+ (jao-w3m-toot nil nil (elt gnus-current-headers 1))
+ (jao-w3m-tweet nil nil (elt gnus-current-headers 1)))))
+
+ (defun jao-gnus-toot-link ()
+ (interactive)
+ (jao-gnus-tweet-link t))
+
+ (defun jao-gnus-mail-link ()
+ (interactive)
+ (let ((txt (jao-gnus--find-link)))
+ (when txt
+ (message-mail-other-window)
+ (message-goto-body)
+ (insert "\n\n" txt)
+ (message-goto-to))))
+ #+END_SRC
+* Keyboard shortcuts
+ #+BEGIN_SRC emacs-lisp
+ (define-key gnus-article-mode-map "\C-ci" 'w3m-view-image)
+ (define-key gnus-article-mode-map "\C-cb" 'jao-w3m-do-browse)
+ (define-key gnus-article-mode-map "i" 'jao-gnus-show-images)
+ (define-key gnus-summary-mode-map "i" 'jao-gnus-show-images)
+ (define-key gnus-article-mode-map "z" 'w3m-lnum-zoom-in-image)
+ (define-key gnus-article-mode-map "\C-ct" 'jao-gnus-tweet-link)
+ (define-key gnus-summary-mode-map "\C-ct" 'jao-gnus-tweet-link)
+ (define-key gnus-article-mode-map "\C-cT" 'jao-gnus-toot-link)
+ (define-key gnus-summary-mode-map "\C-cT" 'jao-gnus-toot-link)
+ (define-key gnus-article-mode-map "\M-g" 'jao-gnus-follow-link)
+ (define-key gnus-summary-mode-map "\M-g" 'jao-gnus-follow-link)
+ (define-key gnus-summary-mode-map "v" 'scroll-other-window)
+ (define-key gnus-summary-mode-map "V" 'scroll-other-window-down)
+ (define-key gnus-summary-mode-map "X" 'jao-gnus-arXiv-capture)
+
+ (major-mode-hydra-define gnus-summary-mode nil
+ ("Browse"
+ (("g" jao-gnus-follow-link "Follow link in emacs")
+ ("G" (lambda () (interactive) (jao-gnus-follow-link t))
+ "Follow link in external browser"))
+ "Capture"
+ (("x" jao-gnus-arXiv-capture "Capture arXiv entry")
+ ("e" jao-gnus-open-enclosure "Add enclosure to playlist")
+ ("E" (jao-gnus-open-enclosure t) "Play enclosure"))
+ "Images"
+ (("i" jao-gnus-show-images "Show images"))
+ "Toot"
+ (("t" jao-gnus-tweet-link "Tweet article")
+ ("T" jao-gnus-toot-link "Toot article"))))
+
+ (major-mode-hydra-define gnus-article-mode nil
+ ("Browse"
+ (("g" jao-gnus-follow-link "Follow link in emacs")
+ ("G" (lambda () (interactive) (jao-gnus-follow-link t))
+ "Follow link in external browser"))
+ "Capture"
+ (("x" jao-gnus-arXiv-capture "Capture arXiv entry")
+ ("e" jao-gnus-open-enclosure "Add enclosure to playlist")
+ ("E" (jao-gnus-open-enclosure t) "Play enclosure"))
+ "Images"
+ (("z" w3m-lnum-zoom-in-image "Zoom image at point")
+ ("I" w3m-view-image "View image at point")
+ ("i" jao-gnus-show-images "Show images"))
+ "Toot"
+ (("t" jao-gnus-tweet-link "Tweet article")
+ ("T" jao-gnus-toot-link "Toot article"))))
+ #+END_SRC