dotfiles

🎜 Clone'em, tweak'em, stick'em in your $HOME 🎝
git clone https://git.kevinlegouguec.net/dotfiles
Log | Files | Refs | README

init.el (13436B)


      1 ;;; -*- lexical-binding: t -*-
      2 
      3 ;;; Externalities.
      4 
      5 ;; user-full-name       from /etc/passwd; set with chfn(1).
      6 ;; user-mail-address    from EMAIL variable; set with ~/.profile,
      7 ;;                      ~/.xsessionrc, DE's convention-du-jour.
      8 
      9 ;; ~/.authinfo.gpg:
     10 ;;      machine imap.gmail.com login LOGIN password PASSWORD port 993
     11 ;;      machine smtp.gmail.com login LOGIN password PASSWORD port 587
     12 
     13 ;;; Þe Olde Setq.
     14 (setq gnus-select-method
     15       '(nnimap "gmail"
     16                ;; TODO: try auth-source-xoauth2-plugin:
     17                ;; (nnimap-authenticator xoauth2)
     18                (nnimap-address "imap.gmail.com")
     19                (nnimap-server-port 993)
     20                (nnmail-expiry-target "nnimap+gmail:[Gmail]/Trash")
     21                (nnmail-expiry-wait immediate))
     22       gnus-secondary-select-methods
     23       '((nntp "archive.lwn.net")
     24         (nntp "news.gmane.io"))
     25 
     26       smtpmail-smtp-server "smtp.gmail.com"
     27       smtpmail-smtp-service 587
     28 
     29       ;; Archival of sent messages.
     30       gnus-gcc-mark-as-read t
     31       ;; The next setting makes the previous one useless; keeping both
     32       ;; for now because I'm not sure which I'll settle for.
     33       gnus-message-archive-group nil
     34 
     35       ;; Groups.
     36       gnus-group-uncollapsed-levels 2
     37 
     38       ;; Summary.
     39       gnus-auto-center-summary nil
     40       gnus-prompt-before-saving t
     41       gnus-summary-line-format "%*%U%R %-11,11&user-date; %B%-23,23f  %s\n"
     42       gnus-summary-dummy-line-format "               ╭                          %S\n"
     43       gnus-summary-make-false-root 'dummy
     44       gnus-sum-thread-tree-root "╭ "
     45       gnus-sum-thread-tree-false-root "┬ "
     46       gnus-sum-thread-tree-single-indent "  "
     47       gnus-sum-thread-tree-indent " "
     48       gnus-sum-thread-tree-single-leaf "╰► "
     49       gnus-sum-thread-tree-leaf-with-other "├► "
     50       gnus-sum-thread-tree-vertical "│"
     51       gnus-thread-sort-functions
     52       '(gnus-thread-sort-by-number
     53         (not gnus-thread-sort-by-most-recent-date))
     54       gnus-user-date-format-alist '(((gnus-seconds-today)
     55                                      . "      %H:%M")
     56                                     ((+ 86400 (gnus-seconds-today))
     57                                      . "Yestd %H:%M")
     58                                     ((* 6 86400)
     59                                      . "  %a %H:%M")
     60                                     ((gnus-seconds-month)
     61                                      . "%a %d")
     62                                     ((gnus-seconds-year)
     63                                      . "%b %d")
     64                                     (t
     65                                      . "%F"))
     66       ;; Articles.
     67       gnus-cite-parse-max-size nil
     68       gnus-header-face-alist
     69       '(("From" nil gnus-header-from)
     70         ("Subject" nil gnus-header-subject)
     71         ("Date" nil eighters-date)
     72         ("Newsgroups:.*," nil gnus-header-newsgroups)
     73         ("" gnus-header-name gnus-header-content))
     74       gnus-sorted-header-list
     75       (list
     76        ;; What, when.
     77        "^Subject:" "^Summary:" "^Keywords:" "^Date:"
     78        ;; Who.
     79        "^From:" "^Organization:" "^Followup-To:" "^To:" "^Cc:" "^Newsgroups:")
     80       gnus-treat-display-smileys nil
     81       ;; Do not fill anything; let visual-line-mode wrap text.
     82       ;;; NB: for format=flowed, there is no setting to say "un-fill
     83       ;;; flowed lines", so we *enable* filling, setting an absurd
     84       ;;; line length limit, in order to un-fill flowed lines.
     85       fill-flowed-display-column most-positive-fixnum
     86       mm-fill-flowed t
     87       ;;; More long-line-folding settings.
     88       gnus-article-unfold-long-headers t
     89       gnus-treat-fill-article nil
     90       gnus-treat-fill-long-lines nil
     91       gnus-treat-fold-headers nil)
     92 
     93 ;;; Window configurations.
     94 
     95 (defvar my/gnus-side-by-side-threshold 160)
     96 
     97 (gnus-add-configuration
     98  '(article
     99    (if (>= (frame-width) my/gnus-side-by-side-threshold)
    100        '(horizontal 1.0
    101                     (summary 1.0 point)
    102                     (article 80))
    103      '(vertical 1.0
    104                 (summary 0.25 point)
    105                 (article 1.0)))))
    106 
    107 (dolist (buf-name '(forward reply reply-yank))
    108   (gnus-add-configuration
    109    `(,buf-name
    110      (if (>= (frame-width) my/gnus-side-by-side-threshold)
    111          '(vertical 1.0
    112                     (summary 0.25)
    113                     (horizontal 1.0
    114                                 (article 0.5)
    115                                 (message 1.0 point)))
    116        '(vertical 1.0
    117                   (summary 0.2)
    118                   (article 0.2)
    119                   (message 1.0 point))))))
    120 
    121 ;;; Summary tweaks.
    122 
    123 (defun my/gnus-toggle-article-wrap ()
    124   (interactive)
    125   (with-current-buffer gnus-article-buffer
    126     (visual-line-mode 'toggle)))
    127 
    128 (defun my/gnus-summary-tweak-keys ()
    129   (keymap-local-set "C-c d v" 'my/gnus-toggle-article-wrap))
    130 
    131 (add-hook 'gnus-summary-mode-hook 'my/gnus-summary-tweak-keys)
    132 
    133 ;; message-subject-re-regexp is used both in Gnus summary buffers to
    134 ;; detect and elide similar subjects in a thread, and by message mode
    135 ;; when replying, to determine what to strip from the subject.
    136 ;;
    137 ;; Some MUAs add cruft to the subject, turning "Re: bug#123: foobar"
    138 ;; into "RE: [External] : Re: bug#1234: foobar", which Debbugs will
    139 ;; then turn into "bug#1234: [External] : Re: bug#1234: foobar".
    140 ;;
    141 ;; The only way I can find to tell the Gnus summary code to
    142 ;; canonicalize all that cruft away is by tweaking this regexp, but
    143 ;; setting its global value causes message-mode to elide stuff it
    144 ;; shouldn't when crafting subjects.  Therefore, chase down the best
    145 ;; Gnus hook for the job, and set the regexp locally.
    146 (defun my/gnus-reply-prefixes ()
    147   (mapcan (lambda (prefix) (list prefix (upcase prefix) (capitalize prefix)))
    148           '("re" "aw" "sv" "fw" "fwd")))
    149 
    150 (setq my/gnus-summary-normalize-subject
    151       (rx-to-string
    152        `(seq bol
    153              (+ (or (seq word-start (or ,@(my/gnus-reply-prefixes)) word-end)
    154                     (seq "bug#" (+ digit))
    155                     (seq "[" (or "External" "SPAM UNSURE") "]"))
    156                 (? (* space) ":") (* space)))))
    157 
    158 (add-hook 'gnus-summary-generate-hook
    159           (lambda ()
    160             (setq-local message-subject-re-regexp
    161                         my/gnus-summary-normalize-subject)))
    162 
    163 (let* ((initials (mapconcat (lambda (s) (substring s 0 1))
    164                             (split-string user-full-name)
    165                             nil))
    166        (sent-prefix (format "%s → " initials)))
    167   (setq gnus-summary-to-prefix sent-prefix
    168         gnus-summary-newsgroup-prefix sent-prefix))
    169 
    170 ;;; Article tweaks.
    171 
    172 (defun my/gnus-article-eschew-tables ()
    173   ;; I set shr-fill-text to nil because I prefer letting
    174   ;; visual-line-mode manage wrapping.  Unfortunately, many HTML
    175   ;; emails rely on <table>s for layouts, and rendering can get ugly.
    176   ;; Work around this by treating <table> & children as any other
    177   ;; <div>.
    178   (make-local-variable 'shr-external-rendering-functions)
    179   (pcase-dolist (`(,tag . ,shr-function)
    180                  '((table . shr-tag-div)
    181                    (thead . shr-tag-div)
    182                    (tbody . shr-tag-div)
    183                    (tr    . shr-tag-ul)
    184                    (th    . shr-tag-li)
    185                    (td    . shr-tag-li)))
    186     (setf (alist-get tag shr-external-rendering-functions) shr-function)))
    187 
    188 (defun my/gnus-article-has-html ()
    189   ;; Hard to tell the difference between
    190   ;; * the variable `gnus-article-mime-handles',
    191   ;; * the function `gnus-article-mime-handles',
    192   ;; * the variable `gnus-article-mime-handle-alist'.
    193   ;;
    194   ;; Stealing debbugs.el's patch-finding logic.
    195   (seq-some
    196    (lambda (handle)
    197      (string= (mm-handle-media-type (cdr handle)) "text/html"))
    198    (gnus-article-mime-handles)))
    199 
    200 (defun my/gnus-article-should-wrap ()
    201   (save-excursion
    202     (message-goto-body)
    203     (let ((should-wrap nil)
    204           (has-html (my/gnus-article-has-html)))
    205       (while-let (((not should-wrap))
    206                   ((not (eobp)))
    207                   (current-line (thing-at-point 'line)))
    208         (setq should-wrap
    209               (and
    210                ;; The line is bigger than the target width.
    211                (> (length current-line)
    212                   (window-width (get-buffer-window gnus-article-buffer)))
    213                ;; The line is not boring (citation, diff addition/removal).
    214                (not (string-match-p "\\`[>+-]" current-line))
    215                ;; Lines that start with spaces are boring, except in
    216                ;; HTML parts: those are choked with <table> tags that
    217                ;; shr left-pads with spaces.
    218                ;; NB: HAS-HTML is a naive heuristic: we are assuming
    219                ;; that "any text/html part is present" means "we are
    220                ;; looking at this text/html part".
    221                (or (not (string-match-p "\\` " current-line)) has-html)))
    222         (forward-line))
    223       should-wrap)))
    224 
    225 (defun my/gnus-article-wrap-maybe ()
    226   ;; Enable visual-line-mode when it helps, i.e. when the message has
    227   ;; long lines that are not part of citations nor patches.
    228   (with-current-buffer gnus-article-buffer
    229    (visual-line-mode
    230     (unless (my/gnus-article-should-wrap) -1))))
    231 
    232 ;; Article setup is tricky.  In order, `gnus-article-prepare'
    233 ;;
    234 ;; (1) calls `gnus-article-setup-buffer', which
    235 ;;     (a) calls `gnus-article-mode', which runs
    236 ;;         gnus-article-mode-hook,
    237 ;;     (b) sets truncate-lines from gnus-article-truncate-lines,
    238 ;;
    239 ;; (2) calls `gnus-display-mime', which may end up calling `mm-shr';
    240 ;;     this can call `shr-tag-table', which turns truncate-lines on
    241 ;;     unconditionally.
    242 ;;
    243 ;; (3) runs gnus-article-prepare-hook.
    244 ;;
    245 ;; Gnus will only run (1a) once, and skip that step when it re-uses
    246 ;; the same *Article* buffer for subsequent articles.  So for our
    247 ;; purposes, we need to
    248 ;;
    249 ;; (Ⅰ) hack the shr rendering functions in mode-hook, before `mm-shr'
    250 ;;     gets to work.
    251 ;; (Ⅱ) call `visual-line-mode' (if needed) in prepare-hook, after
    252 ;;     truncate-lines has been set.
    253 
    254 (add-hook 'gnus-article-mode-hook 'my/gnus-article-eschew-tables)
    255 (add-hook 'gnus-article-prepare-hook 'my/gnus-article-wrap-maybe)
    256 
    257 ;;; MIME display.
    258 (defun my/mm-display-markdown-inline (handle)
    259   (mm-display-inline-fontify handle 'markdown-mode))
    260 
    261 (with-eval-after-load 'mm-decode
    262   ;; bug-gnu-emacs:<jwvzfsnntlq.fsf-monnier+emacs@gnu.org>
    263   (setf (alist-get "text/markdown" mm-inline-media-tests nil nil 'equal)
    264         '(my/mm-display-markdown-inline)))
    265 
    266 ;;; Key bindings.
    267 ;;
    268 ;; m            compose
    269 ;;
    270 ;; Group buffer:
    271 ;;
    272 ;; L            list all groups
    273 ;; RET          view unread mail in group
    274 ;; C-u RET      view all mail in group
    275 ;; g            refresh
    276 ;; G G          search group
    277 ;;
    278 ;; Summary buffer:
    279 ;;
    280 ;; B m          move message to group
    281 ;; / N          fetch new
    282 ;; M-g          refresh (expire, move, fetch new, show unread)
    283 ;; C-u M-g      refresh (expire, move, fetch new, show all)
    284 ;; C-u g        show raw, undecoded message source; g to decode
    285 ;; T h          collapse (hide) thread
    286 ;; T s          expand (show) thread
    287 ;; T k, C-M-k   mark thread as read
    288 ;; M-1 T k      mark thread as unread
    289 ;; r            reply
    290 ;; R            reply (quoting)
    291 ;; S w          reply-all
    292 ;; S W          reply-all (quoting)
    293 ;; C-c C-f      forward
    294 ;; d            mark read
    295 ;; M-u          clear marks (≡ mark unread)
    296 ;; E            expire
    297 ;; #            toggle mark for next action
    298 ;; M-#, M P u   unmark for next action
    299 ;;
    300 ;;      Draft summary buffer:
    301 ;;
    302 ;;      D e     edit draft
    303 ;;
    304 ;; Article buffer:
    305 ;;
    306 ;; o            save attachment at point
    307 ;; K b          add button for inlined MIME part
    308 ;;
    309 ;; Composing:
    310 ;;
    311 ;; C-c C-c      send
    312 ;; C-c C-a      attach
    313 ;; C-c C-f s    change the subject (append "was:")
    314 ;;
    315 ;;; FAQ.
    316 ;;
    317 ;; - how to see *all mails*, not just unread?
    318 ;;      - C-u RET
    319 ;;
    320 ;; - how to do something on a bunch of mail matching a pattern?
    321 ;;      - M P R     ; mark all mails with subjects matching regexp
    322 ;;      - M-& <x>   ; do <x> on all marked mails
    323 ;;
    324 ;; - how to delete mail?
    325 ;;      - E to mark as expired
    326 ;;      - C-u M-g to refresh
    327 ;;
    328 ;; - how to remove groups deleted on the IMAP server?
    329 ;;      - b to iterate over "bogus" groups and remove them
    330 ;;
    331 ;; - how to list most-recent mails on top?
    332 ;;      - cf. gnus-thread-sort-functions
    333 ;;
    334 ;; - how to close a mail without going back to the group list?
    335 ;;      - = to make summary full-screen
    336 ;;
    337 ;; - how to get contact completion?
    338 ;;      - install ebdb from GNU ELPA
    339 ;;      - or just use message-mail-alias-type
    340 ;;
    341 ;; - how to refresh?
    342 ;;      - summary buffer:
    343 ;;          - / N (fetch new)
    344 ;;          - M-g (expire, move, fetch & redisplay)
    345 ;;      - group buffer: g
    346 ;;
    347 ;; - what do all those letters mean?
    348 ;;   (info "(gnus) Marking Articles")
    349 ;;      - O     old ≡ read during previous session
    350 ;;      - R     just read
    351 ;;      - r     manually marked as read
    352 ;;      - A     answered
    353 ;;      - E     expirable
    354 ;;      - G     cancelled (e.g. moved somewhere else)
    355 ;;      - .     unseen
    356 ;;
    357 ;; - how to subscribe to mailing lists?
    358 ;;      - to browse an NNTP server, either
    359 ;;          - hit B in the group buffer, then nntp *some server*
    360 ;;          - or add (nntp "*some server*") to gnus-secondary-methods
    361 ;;      - over the list: u
    362 ;;
    363 ;;; TODO.
    364 ;;
    365 ;; - gnus-summary-line-format (📎 for attachments)
    366 ;;
    367 ;; - how to archive mails and news locally?
    368 ;;
    369 ;; - describe-key is mostly useless in article mode:
    370 ;;   > X runs the command gnus-article-read-summary-keys
    371 ;;
    372 ;; - detect possibly missing attachments from keywords