Моя конфигурация Emacs Nickey Emacs Configuration

;;     _   ________
;;    / | / / ____/___ ___  ____ ___________
;;   /  |/ / __/ / __ `__ \/ __ `/ ___/ ___/
;;  / /|  / /___/ / / / / / /_/ / /__(__  )
;; /_/ |_/_____/_/ /_/ /_/\__,_/\___/____/
screenshot

Я давно использую org-mode для формирования файла инициализации Emacs, периодически переписывая его с нуля и перенося в него только самое необходимое из предыдущей версии. Раньше я комментировал все на английском языке, но в сети и так полно примеров на английском, так что пусть пока будет на русском.

Так как недавно я перебрался с Gnome на i3wm, я стал использовать emacs-daemon и терминальную версию редактора, так что наведение красоты в этот раз пока сюда не попало (кое-что, тем не менее, уже добавлено).

Вот конфигурации других людей, в которых я когда-то почерпнул что-то интересное (их, вообще-то, было много больше, но остальных сейчас не припомню). Порядок - алфавитный:

Этот файл также доступен в моем блоге и на GitHub.

Запуск и инициализация

Персональная информация

Требуется, как минимум, для работы с почтой.

(setq user-full-name    "Nikolay Brovko"
      user-mail-address "i@nickey.ru")

Установка пакетного менеджера straight.el

Давно руки чесались его попробовать, а теперь он мне нужен, чтобы подтягивать свои мелкие пакеты из git-репозиториев. Для этих целей необязательно было выбирать именно его, но а почему нет, собственно?

Код установки - копипаста из README проекта.

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

Настройка репозиториев и use-package

Используем Elpa, Melpa, Melpa Stable и Org.

(setq package-archives
      '(("gnu"          . "https://elpa.gnu.org/packages/")
        ("melpa"        . "https://melpa.org/packages/")
        ("melpa-stable" . "https://stable.melpa.org/packages/")
        ("org"          . "https://orgmode.org/elpa/")))

Загружаем пакет use-package, предварительно установив, если необходимо.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(require 'use-package)

Установка библиотек

Когда требуется быстро набросать функцию, автоматизирующую то или иное действие, наличие этих библиотек под рукой сильно облегчает жизнь.

  • dash.el A modern list api for Emacs. No 'cl required.
  • s.el The long lost Emacs string manipulation library.
  • f.el Modern API for working with files and directories.
(dolist (library '(dash dash-functional s f))
  (eval `(use-package ,library :ensure t)))

Переопределение custom-file

Дабы не захламлять .emacs автоматически генерируемым мусором, перенаправляем его в другом месте.

(setq custom-file "~/.emacs.d/custom.el")
(when (file-exists-p custom-file)
  (load custom-file))

Сохранение пути к конфигу

Дабы иметь возможность удобно обращаться к другим файлам в репозитории и открывать конфиг по горячим клавишам, запомним, где что лежит.

(setq n-nemacs-file (or buffer-file-name load-file-name)
      n-nemacs-dir  (f-dirname n-nemacs-file))

Отключение бекапов и мусора возле рабочих файлов

Переносим все создаваемые редактором временные файлы и бекапы в ~/.emacs.d/backups/. Локфайлы - выключаем.

(setq backup-directory-alist
      '((".*" . "~/.emacs.d/backups/")))

(setq auto-save-file-name-transforms
      '((".*" "~/.emacs.d/backups/" t)))

(setq create-lockfiles nil)

Заменяем yes-or-no-p на y-or-n-p повсеместно

Жизнь слишком коротка, чтобы писать yes или no.

Так и не решил, относить это к инициализации или рабочему процессу, пока пусть будет здесь…

(fset #'yes-or-no-p #'y-or-n-p)

Не показывать стартовый экран

Не знаю, скольким людям он пригодился, как по мне - вещь не особенно полезная.

(setq inhibit-startup-screen 't)

Шрифт в GUI

Все таки периодически запускаю emacs в GUI, поэтому нужно установить фонт.

На ноуте экран меньше, поэтому определяем разрешение и для ноута устанавливаем меньший размер шрифта.

(let* ((default-font-family "JetBrains Mono")
       (default-font-size (if (< (x-display-pixel-width ":0") 1920) 11 13))
       (default-font (format "%s %s" default-font-family default-font-size)))
  (if (functionp #'set-default-font)
      (set-default-font default-font)
    (set-frame-font default-font))
  (add-to-list 'default-frame-alist `(font . ,default-font)))

Тема Dracula

(use-package dracula-theme
  :ensure    t
  :config    (load-theme 'dracula t))

Сокрытие верхнего меню, тулбара и скроллбара

Сначала оставил менюбар, чтобы проще было находить границы окна, но во-первых, он выбивается из темы оформления, а во-вторых, границы окна лучше выделить средствами оконного менеджера, так что опять скрываю.

Тулбар и скроллбар скрываю за компанию - в терминальной версии редактора его все равно нет.

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)

Системный буфер обмена

Поскольку я использую терминальную версию Emacs, kill-ring редактора и системный clipboard ничего друг о друге не знают. Пакет xclip решает эту проблему с помощью одноименной утилиты для командной строки.

(use-package xclip
  :ensure    t
  :config    (xclip-mode 1))

Diminish для сокрытия минорных режимов

Зачастую случается так, что минорных режимов набирается с десяток, и в итоге они сжирают все место в модлайне. Чтобы скрывать очевидные режимы (например, включенные глобально - типа Undo Tree), используем Diminish.

(use-package diminish
  :ensure    t)

all-the-icons для отображения иконок

Наводит красоты в дашборде, treemacs и т. д.

(use-package all-the-icons
  :ensure    t)

Включим также отображение иконок в dired.

(use-package all-the-icons-dired
  :ensure    t
  :hook      (dired-mode . all-the-icons-dired-mode))

Dashboard в качестве стартового экрана

Полезно, чтобы утром быстро войти в курс дела, что делал вчера.

(use-package dashboard
  :ensure    t
  :config
  (setq initial-buffer-choice (lambda () (get-buffer "*dashboard*"))
        dashboard-startup-banner (f-join n-nemacs-dir "nemacs.png")
        dashboard-center-content t
        dashboard-set-init-info nil
        dashboard-set-heading-icons t
        dashboard-set-file-icons t)
  (dashboard-setup-startup-hook))

Рабочий процесс

Пробелы вместо табуляции

Не слушаем Ричарда Хендрикса и беспощадно ставим пробелы вместо табуляции.

(setq-default indent-tabs-mode nil
              tab-width        4)

Удаление слова перед курсором как в bash

Я так и не понял, что удаляет Emacs по умолчанию по нажатию C-w, но меня это не устраивает. Надо либо удалять выделенную область, либо слово перед курсором.

(defun n-kill-region-or-backward-word ()
  (interactive)
  (call-interactively
   (if (use-region-p)
       #'kill-region
     #'backward-kill-word)))

(global-set-key (kbd "C-w") #'n-kill-region-or-backward-word)

Выравнивание значений при присваивании (по символу равенства)

Я стараюсь держать символы равенства выровненными там, где это не нарушает стандарта кодирования языка или проекта. Пример ниже

foo    = "bar";
hello  = "world";
foobar = "baz";
(defun align-to-equals (begin end)
  "Align region to equal signs"
  (interactive "r")
  (align-regexp begin end "\\(\\s-*\\)=" 1 1 ))

(global-set-key (kbd "M-n M-=") #'align-to-equals)

Editorconfig

Используем editorconfig для управления кодировкой, размером и стилем отступов и т. д.

(use-package editorconfig
  :ensure    t
  :diminish  editorconfig-mode
  :config
  (editorconfig-mode 1))

Отключение сворачивания по C-z

В терминале C-z отправляет приложение в фоновый режим, что в случае с емаксом, который сам терминал открыть может, доставляет только лишние неудобства при случайном нажатии. Кто-то предпочитает назначить на это сочетание отмену последнего действия, мне же будет спокойнее, если оно вообще ничего делать не будет.

(global-set-key (kbd "C-z") #'ignore)

Переключение буферов по C-x C-b

bs-show начал использовать еще в первые дни использования Emacs, с тех пор чего только не перепробовал, последней попыткой был helm-buffers-list, но как по мне, это менее удобно, чем открыть список буферов, в котором обычно не более 5-10 штук и клавишами n и p выбрать нужный.

(global-set-key (kbd "C-x C-b") #'bs-show)

Перемещение по буферу с помощью Ace Jump

Использовать клавиши навигации - контрпродуктивно! Ace Jump оцениваешь, попользовавшись им пару дней и поломав привычку перемещаться по тексту линейно.

(use-package ace-jump-mode
  :ensure    t
  :bind      (("C-c SPC" . #'ace-jump-mode)))

Переключение между окнами с помощью Ace Window

В общем-то схожий с Ace Jump воркфлоу для переключения между окнами. Просто переназначаем C-x o, который по дефолту используется для переключения на следующее окно. Поскольку правая рука после нажатия C-x o находится в позиции u i o p, используем эти клавиши в качестве шорткатов для окон.

(use-package ace-window
  :ensure    t
  :bind      ("C-x o" . #'ace-window)
  :config
  (setq aw-keys '(?u ?i ?o ?p ?j ?k ?l ?m)
        aw-ignore-current t))

Подсветка парных скобок

Для лисповых языков, да и не только, переоценить эту функцию очень сложно…

(show-paren-mode 1)

С помощью переменной show-paren-style можно установить способ подсветки:

  • 'expression всё выражение между парными скобками
  • 'parenthesis только сами скобки
  • 'mixed смешанный вариант - в случае, если парная скобка находится в области видимости экрана, будут подсвечены только скобки, в противном случае - все выражение.

Сниппеты с yasnippet

Современные языки и фреймворки, вроде как, сильно сократили количество бойлерплейт-кода. Но, во-первых, не до нуля, а во-вторых, не все.

Добавляем директорию yas-snippets в список директорий для поиска сниппетов. Поскольку она добавляется в начало списка, она же будет путем сохранения для сниппетов, добавляемых с помощью yas-new-snippet.

Раньше каталог назывался просто snippets, но это имя конфликтует с каталогом, создаваемым пакетом yasnippet-snippets по пути ~/.emacs.d/.

(use-package yasnippet
  :ensure t
  :diminish yas-minor-mode
  :config
  (progn
    (add-to-list 'yas-snippet-dirs (f-join n-nemacs-dir "yas-snippets"))
    (yas-global-mode 1)))

Чтобы не создавать все сниппеты руками, подключаем пакет yasnippet-snippets, в который входит множество готовых сниппетов для большого количества мажорных режимов.

(use-package yasnippet-snippets
  :ensure    t
  :after     yasnippet)

Автодополнение скобок, кавычек, etc

Раньше я использовал autopair, но сейчас заглянул к нему в репозиторий и как-то там безрадостно. Случайно наткнулся на smartparens, его и буду пробовать - время покажет.

(use-package smartparens
  :ensure    t
  :diminish  smartparens-mode
  :config
  (progn
    (require 'smartparens-config)
    (smartparens-global-mode 1)))

Автокомплит с company

Сам по себе он мне без надобности, пользуюсь только с LSP, и то не всегда.

(use-package company
  :ensure    t)

Для красивых скриншотов добавлю еще иконки в буфер автокомплита.

(use-package company-box
  :ensure    t
  :hook      (company-mode . company-box-mode))

Управление git-репозиторием с Magit

Magit - пожалуй, лучшая реализация интерфейса к git-репозиторию из всех, что мне попадались.

(use-package magit
  :ensure    t
  :bind      (("C-x g" . #'magit-status)))

ediff создает дополнительный фрейм, что доставляет проблемы с фокусом и компоновкой окон на тайловых оконных менеджерах. Отключаем такое поведение.

(setq ediff-window-setup-function 'ediff-setup-windows-plain)

При использовании GPG-агента возникает проблема с использованием демона. Устанавливаем переменную окружения SSH_AUTH_SOCK вручную.

(when-let ((ssh-auth-sock (s-trim (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket"))))
  (setenv "SSH_AUTH_SOCK" ssh-auth-sock))

Writeroom для работы над текстами

Если в процессе кодинга информация вроде имени открытого файла, текущей git-ветки, положения в документе идут строго на пользу, то при работе над постами в блог, документацией и tex-документами, она только мешает. Writeroom отображает только текущий буфер, прячет модлайн, ограничивает текст по ширине и смещает его в центр окна (не путать с выравниванием по центру). Вкупе с переводом окна в фулл-скрин и увеличением шрифта это сильно помогает сосредоточиться на тексте.

(use-package writeroom-mode
  :ensure    t
  :bind      ("M-n M-w" . #'writeroom-mode))

Treemacs дерево каталогов а-ля Sublime, VSCode, etc

Вообще говоря, я как-то привык жить без него, но почему бы и нет?

(use-package treemacs
  :ensure    t
  :bind      ("M-n M-n" . #'treemacs))

Проверка орфографии

flyspell встроен в Emacs и использует утилиту aspell. Словари надо установить руками, средствами операционной системы

(use-package flyspell
  :hook ((org-mode-hook . flyspell-mode)
         (text-mode-hook . flyspell-mode)
         (markdown-mode-hook . flyspell-mode)))

Untabify для всего буфера

По дефолту untabify работает для выделенной области или текущей строки, что неудобно, когда нужно заменить табы на пробелы во всем буфере.

(defun untabify-buffer ()
  (interactive)
  (untabify (point-min) (point-max)))

Нормализация вертикальных отступов

Когда я вижу оставленные вертикальные отступы по 10 строк в коде, мне хочется отправиться домой к автору коммита для разъяснительной беседы. Ну и для недопущения подобного у себя, удобно использовать в before-save-hook.

(defun group-empty-lines ()
  (interactive)
  (save-excursion
    (replace-regexp "^\n\n*$" "" nil (point-min) (point-max) t)))

Режимы для различных языков программирования и разметки

За пару месяцев их набирается полтора-два десятка, так что буду добавлять по мере использования.

LSP

Language Server Protocol. Позволяет подтащить специфичные для языка фичи без реализации их средствами самого Emacs.

(use-package lsp-mode
  :ensure    t)

(use-package lsp-ui
  :ensure    t)

(use-package company-lsp
  :ensure    t)

(use-package lsp-treemacs
  :ensure    t)

Лиспы

  • lispy-mode

    Paredit не развивается, пробую вместо него сабж

    (use-package lispy
      :ensure    t
      :diminish  lispy-mode
      :hook      ((emacs-lisp-mode . lispy-mode)
                  (scheme-mode . lispy-mode)))
    
  • geiser для Scheme

    Иногда надо покрутить Scheme-подобные языки - Guile, Racket. Geiser, в первую очередь, предоставляет нормальный REPL для них.

    (use-package geiser
      :ensure    t)
    
  • (lambda ()) как (λ ())

    Так код выглядит веселее и компактнее

    (use-package prog-mode
      :hook      ((emacs-lisp-mode
                   scheme-mode) . prettify-symbols-mode))
    

Web-mode для верстки

Незаменимая вещь для работы с html, php, twig и прочими html-подобными файлами. Умеет расставлять отступы, автоматически закрывает открываемые теги, комментирует-раскомментирует блок, переименовывает тег и многое другое.

(use-package web-mode
  :ensure    t
  :config
  (progn
    (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.twig\\'"  . web-mode))
    (add-to-list 'web-mode-engines-alist '("php"  . "\\.php\\'"))))

PHP

Тут и добавить нечего - пользуем php-mode для работы с php-кодом. Требования невысоки - нормальная подсветка синтаксиса, автоматические отступы, открытие документации. По умолчанию использовать стиль psr-2.

(use-package php-mode
  :ensure    t
  :config
  (add-hook 'php-mode-hook #'php-enable-psr2-coding-style))

JavaScript

Вообще говоря, js2 это минорный, а не мажорный режим, но и выносить его за пределы JavaScript-раздела смысла нет.

(use-package js2-mode
  :ensure    t
  :config
  (add-hook 'js-mode-hook #'js2-minor-mode))

VALA

В последнее время редактировал и писал много кода на Vala и, похоже, в будущем еще предстоит, поэтому решил перенести из локального конфига в глобальный.

(defun lsp-register-vala-backend ()
  "Register vala-language-server-alpha lsp backend"
  (add-to-list 'lsp-language-id-configuration '(vala-mode . "vala"))

  (lsp-register-client
   (make-lsp-client :new-connection (lsp-stdio-connection "vala-language-server")
                    :major-modes '(vala-mode)
                    :server-id 'vala-language-server)))

(defun n-vala-mode-hook ()
  (setq indent-tabs-mode nil)
  (setq require-final-newline t)
  (c-set-style "vala")
  (add-hook 'before-save-hook #'untabify-buffer t t)
  (add-hook 'before-save-hook #'delete-trailing-whitespace t t)
  (add-hook 'before-save-hook #'group-empty-lines t t))

(use-package vala-mode
  :ensure    t
  :hook      (vala-mode . lsp)
             (vala-mode . n-vala-mode-hook)
  :config    (lsp-register-vala-backend))

У меня нет времени заняться и оживить vala-mode самостоятельно, а авторы на него немного забили. По c-style самым близким к тому, что нужно, оказался psr2 от php-mode, его и используем.

(c-add-style "vala" (list (if (assoc "psr2" c-style-alist) "psr2" "linux")))

Т. к. в Vala принято использовать Meson для сборки проекта, подключение соответствующего режима положу сюда.

(use-package meson-mode
  :ensure    t)

(use-package meson-build
  :ensure    t
  :straight  (meson-build :type git
                          :repo "https://git.nickey.ru/nickey/meson-build.el"))

(defun meson-build-set-keys ()
  "Hook setting buffer-local Vala and Meson specific keybindings"
  (local-set-key (kbd "C-c C-c") #'compile)
  (local-set-key (kbd "C-c C-r") #'meson-build-run-project)
  (local-set-key (kbd "C-c C-t") #'meson-build-run-tests)
  (local-set-key (kbd "C-c C-g") #'meson-build-run-gdb)
  (local-set-key (kbd "C-c b")   #'meson-build-set-project-bin))

(add-hook 'meson-mode-hook #'meson-build-set-compile-command)
(add-hook 'meson-mode-hook #'meson-build-set-keys)
(add-hook 'vala-mode-hook  #'meson-build-set-compile-command)
(add-hook 'vala-mode-hook  #'meson-build-set-keys)

Еще неплохо было бы автоматически вставлять GNU/GPL заголовок в каждый файл, если лицензия применима для текущего проекта.

(defun n-locate-dominating-file (file name)
  (when-let (dir (locate-dominating-file file name))
    (f-join dir name)))

(defun n-is-gpl-project (path)
  (when-let (copying-file (or (n-locate-dominating-file path "COPYING")
                              (n-locate-dominating-file path "LICENSE")))
    (with-temp-buffer
      (insert-file-contents copying-file)
      (search-forward "GNU GENERAL PUBLIC LICENSE" nil t))))

(defun insert-gpl-header ()
  (when-let (file (buffer-file-name))
    (when (and (not (file-exists-p file)) (n-is-gpl-project file))
      (when-let (snippet (yas-lookup-snippet "gpl3" major-mode t))
        (yas-minor-mode 1)
        (yas-expand-snippet snippet)))))

(add-hook 'vala-mode-hook #'insert-gpl-header t)

YAML

Довольно часто приходится редактировать YAML в различных конфигах. Опять же, от режима требуется, разве что, подсветка синтаксиса и сохранение отступов.

(use-package yaml-mode
  :ensure    t)

Markdown

README.md - наше все.

(use-package markdown-mode
  :ensure    t)

Жизнедеятельность

Самоорганизация

Установка свежего org-mode

Устанавливаем свежую версию org-mode c дополнениями из официального репозитория.

(use-package org-plus-contrib
  :ensure    t
  :defer     t)

Всегда включаем перенос по словам в длинных строках для org-mode (практика показала, что выключать его приходится многократно реже, чем включать).

(add-hook 'org-mode-hook 'visual-line-mode)

Подключаем полезности из contrib.

(require 'org-checklist)
(require 'org-habit)

Настройка шаблонов для org-capture

org-capture это крайне полезная штука, позволяющая быстро делать заметки и комментарии из любого места, сохраняя их в указанных местах. В моем случае org-capture вызывается командой C-c c.

(global-set-key (kbd "C-c c") #'org-capture)

Мои org-файлы обычно лежат в папке ~/.cloud/org, которая, в свою очередь, ведет в NextCloud облако.

(setq n-org-files-dir "~/.cloud/org"
      n-org-inbox     (f-join n-org-files-dir "inbox.org")
      n-org-projects  (f-join n-org-files-dir "projects.org")
      n-org-calendar  (f-join n-org-files-dir "calendar.org")
      n-org-someday   (f-join n-org-files-dir "someday.org")
      n-org-logbook   (f-join n-org-files-dir "logbook.org.gpg"))
(setq org-default-notes-file n-org-inbox)

Шаблоны, которые я использую:

  • i inbox Попадает во Входящие для последующего разбора, схоже с GTD.
  • l logbook Используется для фиксации различных событий и мыслей в шифрованном файле, отдаленно напоминает дневник, только не дневник.
(setq n-capture-templates-dir (f-join n-nemacs-dir "org-templates"))
(setq org-capture-templates
      `(("i" "Plain inbox entry"
         entry  (file+headline ,n-org-inbox     "Входящие")
         (file ,(f-join n-capture-templates-dir "inbox-plain.org")))
        ("l" "Logbook entry"
         entry  (file+datetree+prompt ,n-org-logbook)
         (file ,(f-join n-capture-templates-dir "logbook.org")))))

Настройка org-refile

После того, как заметки попали в Inbox, их периодически нужно разбирать, раскладывая по соответствующим разделам и файлам. Для этого служит механизм org-refile. Настроим целевые файлы, в которые возможен перенос. Сам Inbox по понятным причинам в этот список не входит.

(setq org-refile-targets `((,n-org-projects . (:maxlevel . 2))
                           (,n-org-calendar . (:maxlevel . 2))
                           (,n-org-someday  . (:maxlevel . 2))))

Настройка org-agenda

Когда в заметках наведен порядок, хотелось бы в удобоваримом виде получить список дел на день, неделю или без привязки к календарю. Для этого есть org-agenda. Указываем файлы, из которых нужно собирать заметки и назначаем клавиши вызова.

(setq org-agenda-files (list n-org-inbox n-org-projects n-org-calendar))
(global-set-key (kbd "C-c a") #'org-agenda)

Настройка org-archive

Удаление выполненных или отмененных дел - плохая практика т. к. иногда (не так уж редко) бывает нужно уточнить, что именно было сделано, когда, если отменено - то почему и т. д. Короче говоря, айтишники тяжело расстаются с метаинформацией. Поэтому вместо удаления лучше использовать org-archive, который позволяет перемещать ставшие ненужными заметки или деревья в отдельный файл, добавляя к ним информацию о дате архивирования, предыдущем местонахождении и др.

Чтобы файл не мозолил глаза, я использую скрытый файл .archive.org, а заметки в нем раскладываются по разделам с именами файлов, из которых они были перемещены.

(setq org-archive-location (f-join n-org-files-dir
                                   ".archive.org::** Из файла %s"))

Метод помидора (pomidor)

По правде сказать, я не так часто пользуюсь этим методом, но иногда все же пользуюсь. Поэтому пусть будет на готове.

(use-package pomidor
  :ensure    t
  :bind      (("M-n M-p" . #'pomidor))
  :config
  (setq pomidor-play-sound-file #'ignore
        alert-default-style     'libnotify
        pomidor-graph-char      10074))

Почта (Gnus)

Вообще, для работы с почтой я использую neomutt. Но поскольку я подписан на множество списков рассылки, где часто присылают код, либо самому приходится отправлять фрагменты кода, сразу использовать редактор бывает очень удобно.

Извлечением почты у меня занимается offlineimap, каждые две минуты по крону забирающий почту с сервера по IMAP и аккуратно складывающий ее в папку ~/.mail в формате Maildir. Помимо того, что это позволяет более полноценно работать с почтой, выбирая инструмент под задачу (иногда просто шелл), так появляется возможность разбирать почту из оффлайна - для десктопа так себе достижение, а для ноутбука бывает очень полезно.

(setq gnus-select-method '(nnmaildir "nickey" (directory "~/.mail")))

Ну а поскольку можно разбирать почту в оффлайне, почему бы ее в оффлайне не писать? Я использую msmtp для отправки почты. В пакете с msmtp идут скрипты msmtp-enqueue.sh, msmtp-listqueue.sh и msmtp-runqueue.sh для работы с очередью отправки. Использую msmtp-enqueue для отправки почты, а msmtp-runqueue запускается по крону для отправки почты.

(setq message-send-mail-function #'message-send-mail-with-sendmail
      sendmail-program           "~/.local/bin/msmtp-enqueue")

По умолчанию Gnus начинает хозяйничать в домашней папке и создавать в ней папки Mail и News. Спрячем их в глубинах ~/.emacs.d.

(setq message-directory  "~/.emacs.d/mail/"
      gnus-directory     "~/.emacs.d/news/"
      nnfolder-directory "~/.emacs.d/mail/archive/")

Недавно я отказался от использования Disqus, равно как и любых других видов обратной связи на сайте.

Если вам есть что сказать по поводу прочитанного - можете написать на мою основную электронную почту i@nickey.ru, на данный момент это самый надежный способ связаться со мной.