600 lines
23 KiB
Org Mode
600 lines
23 KiB
Org Mode
#+TITLE: Mike Gerwitz's Emacs Mail Configuration
|
|
#+AUTHOR: Mike Gerwitz
|
|
#+EMAIL: mtg@gnu.org
|
|
|
|
Before beginning on my Emacs-related mail configuration, I want to
|
|
shout out to my previous e-mail cl that served me well for so many
|
|
years: [[http://www.mutt.org/][Mutt]]. My [[https://gitlab.com/mikegerwitz/dotfiles/blob/master/muttrc][personal Mutt configuration]] is still publicly
|
|
available.
|
|
|
|
The reason that an Emacs MUA won out was because the lure and
|
|
flexibility of a client written in a Lisp, and written to be easily
|
|
customized and hooked in a Lisp, is powerful. But it's not for
|
|
everyone; I still recommend Mutt to others.
|
|
|
|
As always with my code, I use lexical variable binding:
|
|
|
|
#+BEGIN_SRC emacs-lisp :padline no
|
|
;; -*- lexical-binding: t -*-
|
|
#+END_SRC
|
|
|
|
* Identity
|
|
My identity isn't as trivial as it sounds, since I may be sharing this
|
|
configuration with multiple environments. But I'm pretty sure my name
|
|
won't be changing:
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq-default user-full-name "Mike Gerwitz")
|
|
#+END_SRC
|
|
|
|
My e-mail address depends on who I am representing (e.g. myself or my
|
|
employer). I therefore defer these configuration options to a
|
|
configurable value that specifies a local profile:
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defcustom mtg/mail-profile
|
|
nil
|
|
"Function used to perform mail setup
|
|
The function should set all necessary mail options."
|
|
:type 'function
|
|
:group 'mtg)
|
|
|
|
;;(funcall 'mtg/mail-profile)
|
|
#+END_SRC
|
|
|
|
That said, there are certain settings that are reasonable defaults;
|
|
they can be overridden if needed.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(require 'smtpmail)
|
|
(setq send-mail-function 'smtpmail-send-it
|
|
message-send-mail-function 'smtpmail-send-it
|
|
smtpmail-debug-info t)
|
|
#+END_SRC
|
|
|
|
** GNU Profile
|
|
Nearly all of my communications are related to software. As a member
|
|
of the GNU Project, I'm both proud and want to do my best to bring
|
|
attention to it. Communications using this address do _not_
|
|
necessarily mean that I'm speaking on behalf of the GNU Project.
|
|
|
|
GNU hosts a POP server on =fencepost=. I have a =fetchmail= cronjob running
|
|
on my server that periodically fetches mail from fencepost onto my own
|
|
mailserver, to which I connect via IMAP; all my mail is then conveniently
|
|
mixed into one account, and all GNU mail is also run through my Sieve rules
|
|
and any other scripts.
|
|
|
|
For sending mail, privacy is an important consideration: I really do not
|
|
want my home IP Address and hostname appearing in the header of every e-mail
|
|
that I send. I have the option if running Emacs using =torify=, but that is
|
|
far too slow for receiving the amount of e-mail that I sift through.[fn:Of
|
|
course I could set up a local IMAP server that downloads my mail
|
|
periodically to avoid this---and I have done that in the past---but for
|
|
reasons I do not want to get into right now, I have stopped using it.] The
|
|
other option is a SOCKS proxy for SMTP only, but it's not obvious to me how
|
|
to do that, so I'll defer further research for another time. This leaves a
|
|
SOCKS proxy in general, or tunneling.
|
|
|
|
I have opted for an SSH tunnel. This has a couple benefits: my normal
|
|
network rules will apply for reading mail---for which privacy is not a
|
|
concern---and, since I'm connecting to =localhost=, I cannot forget to
|
|
invoke Emacs in a special way: the connection will fail if I do not have a
|
|
tunnel set up.[fn:For example, running =torify emacs= or =proxychains emacs=
|
|
can yield a good result, but if I forget to do so, that's a problem.]
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun mtg/mail-prof/gnu ()
|
|
"Mail profile for GNU Project"
|
|
(setq user-mail-address "mtg@gnu.org"
|
|
smtpmail-smtp-server "localhost" ; tunnel
|
|
smtpmail-smtp-service 5587
|
|
|
|
gnus-select-method '(nnimap "mail.mikegerwitz.com"
|
|
(nnimap-inbox "INBOX")
|
|
(nnimap-record-commands t)
|
|
(nnimap-stream tls))))
|
|
#+END_SRC
|
|
|
|
* <<<Gnus>>>
|
|
[[gnus.org][Gnus]] is a popular message reader (that is, newsgroups and the like, in
|
|
addition to E-mail) for Emacs. It is highly customizable via user
|
|
options and hooks. Given that it is written in Elisp, it is also
|
|
customizable through redefining or advising existing functions.
|
|
|
|
Of course, that's assuming that I know what the hell I'm doing. I'd
|
|
like to think that I do.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
;; prevents annoying prompts that try to save your ass from doing
|
|
;; something stupid
|
|
(setq gnus-novice-user nil
|
|
gnus-interactive-exit nil)
|
|
#+END_SRC
|
|
|
|
** Receiving Mail
|
|
The notion of a "large" newsgroup (or mailbox) has changed over the
|
|
years as network connections have continued to improve. With that
|
|
said, some hosts do better than others, some ISPs do better than
|
|
others, and some user choices may have an impact. For example, I use
|
|
Tor for many communications.
|
|
|
|
I have found that 500 messages is a decent amount not only for
|
|
preventing fetching too much data, but to help grok it as well. More
|
|
messages can always be retrieved (using =/ N=).
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq-default gnus-large-newsgroup 500)
|
|
#+END_SRC
|
|
|
|
This would be less of a concern if Emacs were not single-threaded, or
|
|
if Gnus handled the message retrieval in another process or
|
|
thread.
|
|
|
|
** Writing and Sending Mail
|
|
Nearly every conversation I have with someone online is via e-mail; I
|
|
do not use any "social media" websites, and any websites that I do use
|
|
for communication have, or would do well to have, an e-mail interface.
|
|
|
|
*** Replying
|
|
There are important considerations when replying to
|
|
mail. Specifically, when replying, you would do best to provide
|
|
proper unambiguous context and a summary reference for both the
|
|
recipient and any readers of the message. There are [[https://en.wikipedia.org/wiki/Posting_style][plenty of
|
|
opinions on posting style]], but the de-facto standard in technical
|
|
discussions (and discussions with reasonable human beings) is the
|
|
interleave style, whereby responses follow the appropriate /portion/
|
|
of quoted text, followed (potentially) by more quotes and
|
|
replies.
|
|
|
|
The foundation for a good reply is a good quote. I enjoyed Mutt's
|
|
default style, which included a timestamp in the quote heading, which
|
|
I find important. Further, Gnus adds an empty line after the heading,
|
|
which I do not like, as I believe it makes the block quotations more
|
|
difficult to grok at first glance.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq message-citation-line-function
|
|
'message-insert-formatted-citation-line
|
|
|
|
message-citation-line-format
|
|
"On %a, %b %d, %Y at %H:%M:%S %z, %N wrote:"
|
|
|
|
gnus-face-9
|
|
'gnus-face-tree-marker)
|
|
#+END_SRC
|
|
|
|
A proper reply then involves first placing a greeting, apropos, or
|
|
summary above the quote heading, and then inserting replies within
|
|
certain parts of the quote block (creating block fragments), and
|
|
deleting fragments that are unneeded or inapplicable for the reply.
|
|
|
|
*** Importing Messages
|
|
Some messages may be written externally (e.g. generated by a program,
|
|
or even written by another person). I provide functions to import
|
|
them as articles into a group.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun gnus-import-article-quick (group file)
|
|
"Import FILE as an article into GROUP.
|
|
FILE is imported as-is, without any assurances that it constitutes a valid
|
|
message.
|
|
Return (FILE . article-id) pair."
|
|
(interactive "sGroup: \nfImport file: ")
|
|
(with-current-buffer (gnus-get-buffer-create " *import file*")
|
|
(erase-buffer)
|
|
(nnheader-insert-file-contents file)
|
|
(let* ((result (gnus-request-accept-article group nil t t))
|
|
(article-id (cdr result)))
|
|
(kill-buffer (current-buffer))
|
|
`(,file . ,article-id))))
|
|
|
|
|
|
(defalias 'gnus-import-draft-quick
|
|
(apply-partially 'gnus-import-article-quick "nndraft:drafts"))
|
|
#+END_SRC
|
|
|
|
Git is able to generate a patch series for sending commits via
|
|
e-mail. This function helps to import them into Gnus for manual
|
|
editing and sending, rather than using its own mail facilities for
|
|
sending the messages automatically.
|
|
|
|
These functions may be buggy; I seldom use them.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
;; TODO: gracefully fail when no wildcard matches
|
|
(defun gnus-import-patch-set (path &optional edit patch-ext)
|
|
(interactive "DPath: \nP")
|
|
(let* ((ext (or patch-ext ".patch"))
|
|
(result (mapcar 'gnus-import-draft-quick
|
|
(file-expand-wildcards (concat path "/*" ext))))
|
|
(root-id (cdar result)))
|
|
(gnus-group-read-group t nil "nndraft:drafts")
|
|
(gnus-summary-goto-article root-id t t)
|
|
(when edit
|
|
(gnus-summary-edit-article))
|
|
result))
|
|
|
|
(defun gnus-create-patch-set (repo-path topic mainline)
|
|
(interactive "DRepository path: \nsTopic branch: \nsMainline: ")
|
|
(cd repo-path)
|
|
(call-process "git"
|
|
nil
|
|
(list (gnus-get-buffer-create " *create-patch-set*") t)
|
|
nil
|
|
"format-patch"
|
|
(concat mainline ".." topic))
|
|
(gnus-import-patch-set repo-path t))
|
|
#+END_SRC
|
|
|
|
*** Archiving
|
|
I archive my sent messages to both reference and reflect upon what I
|
|
have said. But I don't want to feel obligated to re-read what I
|
|
wrote!
|
|
|
|
#+BEGIN_SRC
|
|
(setq gnus-gcc-mark-as-read 1)
|
|
#+END_SRC
|
|
|
|
** Reading Mail
|
|
The vast majority of my interaction with other human beings is through
|
|
my MUA; its presentation is important. I have not customized it as
|
|
heavily as have [[https://gitlab.com/mikegerwitz/dotfiles/blob/master/muttrc][I have Mutt]] (yet), but it's a start.
|
|
|
|
*** Headers
|
|
Headers are important: they convey metadata that helps to provide
|
|
context and validity to a message. Certain headers I use for
|
|
cross-referencing externally, such as =Message-ID=.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-sorted-header-list '("^From:"
|
|
"^Newsgroups:"
|
|
"^Subject:"
|
|
"^Date:"
|
|
"^Envelope-To:"
|
|
"^Followup-To:"
|
|
"^Reply-To:"
|
|
"^Organization:"
|
|
"^Summary:"
|
|
"^Abstract:"
|
|
"^Keywords:"
|
|
"^To:"
|
|
"^[BGF]?Cc:"
|
|
"^Posted-To:"
|
|
"^List-Id:"
|
|
"^Mail-Copies-To:"
|
|
"^Mail-Followup-To:"
|
|
"^Apparently-To:"
|
|
"^Resent-From:"
|
|
"^User-Agent:"
|
|
"^X-detected-operating-system:"
|
|
"^Message-ID:"
|
|
"^References:"
|
|
"^Gnus-Warning:")
|
|
gnus-visible-headers (mapconcat 'identity
|
|
gnus-sorted-header-list
|
|
"\\|"))
|
|
#+END_SRC
|
|
|
|
Considering that I just expressed my interest in headers, it would
|
|
stand to reason that I would not want them stripped upon saving a
|
|
message somewhere:
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-save-all-headers t)
|
|
#+END_SRC
|
|
|
|
*** Signature Verification
|
|
Always try to verify GPG/PGP-signed messages when possible. If this fails
|
|
because the key is not available, the message conveniently displays the key
|
|
id.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq mm-verify-option 'always)
|
|
#+END_SRC
|
|
|
|
*** Message Formats
|
|
I like plain text. It is the universal language: all standard Unix
|
|
utilities are made to operate on plain text; they can be manipulated
|
|
and piped to other utilities to create sophisticated processes for
|
|
operating on it. It also views well on a terminal, on which I live.
|
|
|
|
I therefore discourage use of HTML e-mails. Gnus is able to render
|
|
HTML emails on a terminal fairly well, but that is not what I want; I
|
|
don't want to read your HTML e-mails at all.
|
|
|
|
Some people do send me HTML-only e-mails; in that case, there's the
|
|
option of forcing the HTML rendering, or viewing the e-mail in my web
|
|
browser. You know---the place where HTML belongs.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
;; >:@
|
|
(setq-default mm-discouraged-alternatives '("text/html"))
|
|
#+END_SRC
|
|
|
|
*** Groups
|
|
Group lines are displayed as follows:
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-group-line-format
|
|
"%M%m%S%5,5y/%-5,5t %*%B%-40,40g %ud\n")
|
|
#+END_SRC
|
|
|
|
**** Sorting
|
|
It makes sense for me to have the most read groups appear at the top
|
|
of my group list. Ranks will be adjusted and the groups re-sorted
|
|
after returning from summary mode.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-groups-sort-groups
|
|
'gnus-group-sort-by-rank)
|
|
|
|
(add-hook 'gnus-summary-exit-hook
|
|
'gnus-summary-bubble-group)
|
|
|
|
(add-hook 'gnus-summary-exit-hook
|
|
'gnus-group-sort-groups-by-rank)
|
|
#+END_SRC
|
|
|
|
**** Topics
|
|
I subscribe to a number of mailing lists, and further organize my mail
|
|
into a number of groups; it helps to have them organized
|
|
hierarchically. Gnus offers a "topic" minor mode that offers this
|
|
feature.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'gnus-group-mode-hook
|
|
'gnus-topic-mode)
|
|
#+END_SRC
|
|
|
|
**** Last Visit Timestamp
|
|
On a similar note, it's also helpful to know when I last looked at a
|
|
group. Sometimes. Not often.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'gnus-select-group-hook
|
|
'gnus-group-set-timestamp)
|
|
#+END_SRC
|
|
|
|
The default timestamp format for =%d= is ISO 8601, which isn't very useful,
|
|
because it requires too much effort to visually parse. In the definition of
|
|
=gnus-group-line-format= above, there is a format specifier =%ud=, which is
|
|
user-defined; I define it here (largely derived from the [[info:gnus][Gnus manual]] Gnus manual):
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun gnus-user-format-function-d (headers)
|
|
(let ((time (gnus-group-timestamp gnus-tmp-group)))
|
|
(if time
|
|
(format-time-string "%a %d %b %Y, %T" time)
|
|
"")))
|
|
#+END_SRC
|
|
|
|
*** Summary Mode
|
|
Gnus' summary mode displays a list of messages. The proper way to
|
|
display messages is in threads, which display responses
|
|
hierarchically. This is especially important for discussions in
|
|
mailing lists, as they can get incredibly lengthy, there can be many
|
|
discussions happening concurrently, and it's easy to lose context.
|
|
|
|
My tree display is inspired by Mutt.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
;; mutt-inspired tree display
|
|
(setq gnus-summary-line-format "%U%R%z %d %-23,23f (%4,4L) %*%9{%B%}%s\n"
|
|
gnus-sum-thread-tree-root ""
|
|
gnus-sum-thread-tree-false-root "──> "
|
|
gnus-sum-thread-tree-leaf-with-other "├─> "
|
|
gnus-sum-thread-tree-vertical "│ "
|
|
gnus-sum-thread-tree-single-leaf "└─> ")
|
|
#+END_SRC
|
|
|
|
When entering summary mode by selecting a group with
|
|
=gnus-topic-read-group= (default =SPC=, as opposed to
|
|
=gnus-topic-select-group=, which defaults to =RET=), an article
|
|
(message) is selected automatically for reading. I want to see any
|
|
unseen articles first, otherwise unread (the latter there may be many
|
|
of, such as in groups associated with high-volume mailing lists).
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-auto-select-subject
|
|
'unseen-or-unread)
|
|
#+END_SRC
|
|
|
|
**** Articles
|
|
An /article/ in Gnus terminology, as far as we're concerned here, is a
|
|
message. When reading, hitting =SPC= will (by default) scroll down a
|
|
page. When reaching the end of an article, the next =SPC= press will
|
|
silently move to the next article.
|
|
|
|
This is a problem, because the previous message may end close to the
|
|
bottom of the window, and then it may not be immediately apparent that
|
|
you are reading a new message. I've been bitten by this before, and
|
|
it can be profoundly confusing. Maybe not for you, but I don't like
|
|
it.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-summary-stop-at-end-of-message t)
|
|
#+END_SRC
|
|
|
|
**** Sparse Threads
|
|
I choose to allow Gnus to hide articles that have been read, which
|
|
helps keep the groups clean looking and easier to grok new
|
|
material. But threads---especially on mailing lists---may be very
|
|
long, and may go on for months. The context of the parent thread is
|
|
very important.
|
|
=A T= can be used to fetch the full thread (the best it can). But
|
|
that can be overkill in cases where threads are quite large. Gnus has
|
|
a concept of "sparse" threads, in which it will attempt to build
|
|
a partial thread of parent messages (even if they are read), and will
|
|
even leave gaps where it detects missing messages (e.g. off-list
|
|
replies). The latter alone is useful in avoiding confusion.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-build-sparse-threads 'some)
|
|
#+END_SRC
|
|
|
|
Building threads on its own isn't a trivial task; you'd think that
|
|
looking at the =References= header would be enough, but that breaks if
|
|
somebody posts a message with a broker MUA or newsreader. And I can
|
|
tell you from experience that this is not as infrequent as I would
|
|
like, even on technical mailing lists.
|
|
|
|
So, even though building threads by subject is not always accurate, it
|
|
will have to do. This is the default behavior.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-summary-thread-gathering-function
|
|
'gnus-gather-threads-by-subject)
|
|
#+END_SRC
|
|
|
|
|
|
*** Window Layout
|
|
My display width permits and article view that contains a tree view
|
|
beside the summary, with the article rendered below both.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(gnus-add-configuration
|
|
'(article
|
|
(horizontal 1.0
|
|
(vertical 0.50
|
|
(summary 1.0 point)
|
|
(tree 0.25))
|
|
(article 1.0))))
|
|
#+END_SRC
|
|
|
|
The =tree= mention above refers to the tree buffer:
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq gnus-use-trees t
|
|
gnus-generate-tree-function 'gnus-generate-vertical-tree
|
|
gnus-tree-minimize-window nil)
|
|
#+END_SRC
|
|
|
|
** Keybindings
|
|
I come from the land of Mutt, and I appreciate the concise keybindings
|
|
that it provides for many operations. I duplicate some of those here.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'gnus-group-mode-hook
|
|
(lambda ()
|
|
(local-set-key "j" 'gnus-group-next-unread-group)
|
|
(local-set-key "k" 'gnus-group-prev-unread-group)
|
|
|
|
;; re-bind jump (originally `j')
|
|
(local-set-key "\M-j" 'gnus-group-jump-to-group)))
|
|
|
|
(add-hook 'gnus-summary-mode-hook
|
|
(lambda ()
|
|
;; `t' by default toggles headers, which we mapped above
|
|
(local-set-key "t" 'gnus-summary-toggle-processable)
|
|
|
|
;; The original keybindings are dangerous for a vim user! They
|
|
;; are still accessible, respectively, via `G j'; `M k'; and
|
|
;; `TAB' within the article buffer.
|
|
(local-set-key "j" 'next-line)
|
|
(local-set-key "k" 'previous-line)
|
|
(local-set-key "\t" 'gnus-summary-next-unread-subject)
|
|
|
|
;; mutt uses `s' for "save", which can be used to move between
|
|
;; IMAP folders (in this case, groups)
|
|
(local-set-key "\C-s" 'gnus-summary-isearch-article)
|
|
(local-set-key "s" 'gnus-summary-move-article)
|
|
|
|
;; the original has other bindings
|
|
(local-set-key "d" 'gnus-summary-mark-as-expirable)))
|
|
|
|
(add-hook 'gnus-article-mode-hook
|
|
(lambda ()
|
|
;; consistency with summary buffer (and mutt)
|
|
(local-set-key "h" 'gnus-summary-toggle-header)
|
|
(local-set-key "v" 'gnus-article-view-part)))
|
|
#+END_SRC
|
|
|
|
The =t= keybinding in Mutt toggles marks, but Gnus offers no function
|
|
to do so; I provide one via =gnus-summary-toggle-processable=:
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun gnus-summary-toggle-processable (n)
|
|
"Toggle process mark on the next N articles.
|
|
If N is negative, mark backward instead; consistent with behavior of
|
|
`gnus-summary-mark-as-processable'."
|
|
(interactive "p")
|
|
(cl-labels ((next (n direction article)
|
|
(when (and (> n 0) article)
|
|
;; toggle article (this also updates point, selecting
|
|
;; the next article if available)
|
|
(funcall
|
|
(if (memq article gnus-newsgroup-processable)
|
|
'gnus-summary-unmark-as-processable
|
|
'gnus-summary-mark-as-processable)
|
|
direction)
|
|
|
|
;; process next (the above call already selected the
|
|
;; next article, so we don't have the return value;
|
|
;; instead, assume that no other articles are
|
|
;; available if the article at point matches the
|
|
;; previously processed article)
|
|
(let ((next-article (gnus-summary-article-number)))
|
|
(unless (eq next-article article)
|
|
(next (1- n) direction next-article))))))
|
|
(next (abs n)
|
|
(if (< n 0) -1 1)
|
|
(gnus-summary-article-number)))
|
|
n)
|
|
#+END_SRC
|
|
|
|
* Contacts
|
|
The majority of my online communication is done via e-mail. Over the
|
|
years---especially on mailing lists---it's easy to accumulate a lot of
|
|
contacts, and it's easy to forget who people are. So, I need the ability to
|
|
not only store names and e-mail addresses (and auto-complete them!), but
|
|
also notes about the person, so that I can remember who they are or other
|
|
useful information about them.
|
|
|
|
Emacs comes with BBDB---the "Insidious" Big Brother Database---which keeps a
|
|
database of contacts. I won't pretend that I know how to use it very well,
|
|
but I'll do by best to learn.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(bbdb-initialize 'gnus 'message)
|
|
|
|
;; this is the key for fixing a brokern BBDB3+Gnus integration
|
|
(setq bbdb-mua-update-interactive-p
|
|
'(query . create))
|
|
#+END_SRC
|
|
|
|
In order to build a comprehensive database, I want contacts to be added with
|
|
ease; en masse if need be.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(bbdb-mua-auto-update-init 'gnus 'message)
|
|
|
|
;; return more than just the first address of a message
|
|
(setq bbdb-message-all-addresses t)
|
|
#+END_SRC
|
|
|
|
BBDB displays a window ("popup") when adding/editing an entry, or visiting
|
|
articles with known entries, from Gnus (for any MUA it's initialized for,
|
|
for that matter). The default size shares window space evenly with all
|
|
others---far too large.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq bbdb-pop-up-window-size 0.15
|
|
bbdb-mua-pop-up-window-size 0.15)
|
|
#+END_SRC
|
|
|
|
* Command Line
|
|
I generally invoke Gnus in a fresh Emacs process, for various reasons that I
|
|
won't get into here right now. To make this a bit easier, I add a =gnus=
|
|
command switch that immediately invokes Gnus and then kills Emacs once it's
|
|
done.
|
|
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-to-list
|
|
'command-switch-alist
|
|
'("gnus" . (lambda (&rest ignore)
|
|
(add-hook 'emacs-startup-hook 'gnus-unplugged t)
|
|
(add-hook 'gnus-after-exiting-gnus-hook
|
|
'save-buffers-kill-emacs))))
|
|
#+END_SRC
|