[emacs] port over many changes/enhancements from straight-exp
[~bandali/configs] / init.org
index e2c87b3..505a793 100644 (file)
--- a/init.org
+++ b/init.org
@@ -9,9 +9,21 @@
 :END:
 
 This org file is my literate configuration for GNU Emacs, and is
 :END:
 
 This org file is my literate configuration for GNU Emacs, and is
-tangled to [[./init.el][init.el]]. Packages are installed and managed using [[https://github.com/emacscollective/borg][Borg]].
-
-** Installation
+tangled to [[./init.el][init.el]]. Packages are installed and managed using
+[[https://github.com/emacscollective/borg][Borg]]. Over the years, I've taken inspiration from configurations of
+many different people. Some of the configurations that I can remember
+off the top of my head are:
+
+- [[https://github.com/dieggsy/dotfiles][dieggsy/dotfiles]]: literate Emacs and dotfiles configuration, uses
+  straight.el for managing packages
+- [[https://github.com/dakra/dmacs][dakra/dmacs]]: literate Emacs configuration, using Borg for managing
+  packages
+- [[http://pages.sachachua.com/.emacs.d/Sacha.html][Sacha Chua's literate Emacs configuration]]
+- [[https://github.com/dakrone/eos][dakrone/eos]]
+- Ryan Rix's [[http://doc.rix.si/cce/cce.html][Complete Computing Environment]] ([[http://doc.rix.si/projects/fsem.html][about cce]])
+- [[https://github.com/jwiegley/dot-emacs][jwiegley/dot-emacs]]: nix-based configuration
+- [[https://github.com/wasamasa/dotemacs][wasamasa/dotemacs]]
+- [[https://github.com/hlissner/doom-emacs][Doom Emacs]]
 
 I'd like to have a fully reproducible Emacs setup (part of the reason
 why I store my configuration in this repository) but unfortunately out
 
 I'd like to have a fully reproducible Emacs setup (part of the reason
 why I store my configuration in this repository) but unfortunately out
@@ -22,12 +34,33 @@ to my init time; which is unacceptable for me: I use Emacs as my
 window manager (via EXWM) and coming from bspwm, I'm too used to
 having fast startup times.
 
 window manager (via EXWM) and coming from bspwm, I'm too used to
 having fast startup times.
 
+** Installation
+
+To use this config for your Emacs, first you need to clone this repo,
+then bootstrap Borg, tell Borg to retrieve package submodules, and
+byte-compiled the packages. Something along these lines should work:
+
+#+begin_src sh :tangle no
+git clone https://github.com/aminb/dotfiles ~/.emacs.d
+cd ~/.emacs.d
+make bootstrap-borg
+make bootstrap
+make build
+#+end_src
+
 * Contents                                                   :toc_1:noexport:
 
 - [[#about][About]]
 - [[#header][Header]]
 - [[#initial-setup][Initial setup]]
 - [[#core][Core]]
 * Contents                                                   :toc_1:noexport:
 
 - [[#about][About]]
 - [[#header][Header]]
 - [[#initial-setup][Initial setup]]
 - [[#core][Core]]
+- [[#borg-essentials][Borg's =layer/essentials=]]
+- [[#editing][Editing]]
+- [[#syntax-spell-checking][Syntax and spell checking]]
+- [[#programming-modes][Programming modes]]
+- [[#emacs-enhancements][Emacs enhancements]]
+- [[#email][Email]]
+- [[#blogging][Blogging]]
 - [[#post-initialization][Post initialization]]
 - [[#footer][Footer]]
 
 - [[#post-initialization][Post initialization]]
 - [[#footer][Footer]]
 
@@ -39,7 +72,7 @@ having fast startup times.
 ** First line
 
 #+begin_src emacs-lisp :comments none
 ** First line
 
 #+begin_src emacs-lisp :comments none
-;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t ; eval: (view-mode 1)-*-
+;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t; eval: (view-mode 1) -*-
 #+end_src
 
 Enable =view-mode=, which both makes the file read-only (as a reminder
 #+end_src
 
 Enable =view-mode=, which both makes the file read-only (as a reminder
@@ -50,7 +83,7 @@ file.
 ** License
 
 #+begin_src emacs-lisp :comments none
 ** License
 
 #+begin_src emacs-lisp :comments none
-;; Copyright (C) 2018  Amin Bandali <amin@aminb.org>
+;; Copyright (C) 2018  Amin Bandali <bandali@gnu.org>
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
@@ -71,39 +104,17 @@ file.
 #+begin_src emacs-lisp :comments none
 ;;; Commentary:
 
 #+begin_src emacs-lisp :comments none
 ;;; Commentary:
 
-;; Emacs configuration of Amin Bandali, computer scientist and functional
-;; programmer.
+;; Emacs configuration of Amin Bandali, computer scientist, functional
+;; programmer, and free software advocate.
 
 ;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
 #+end_src
 
 
 ;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
 #+end_src
 
-** Naming conventions
-
-The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s conventions, found
-[[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]]. Naturally, I use my initials, =ab=, instead of =doom=.
-
-#+begin_src emacs-lisp :comments none
-;; Naming conventions:
-;;
-;;   ab-...   public variables or non-interactive functions
-;;   ab--...  private anything (non-interactive), not safe for direct use
-;;   ab/...   an interactive function; safe for M-x or keybinding
-;;   ab:...   an evil operator, motion, or command
-;;   ab|...   a hook function
-;;   ab*...   an advising function
-;;   ab@...   a hydra command
-;;   ...!     a macro
-#+end_src
-
 * Initial setup
 :PROPERTIES:
 :CUSTOM_ID: initial-setup
 :END:
 
 * Initial setup
 :PROPERTIES:
 :CUSTOM_ID: initial-setup
 :END:
 
-#+begin_src emacs-lisp :comments none
-;;; Code:
-#+end_src
-
 ** Emacs initialization
 
 I'd like to do a couple of measurements of Emacs' startup time. First,
 ** Emacs initialization
 
 I'd like to do a couple of measurements of Emacs' startup time. First,
@@ -111,10 +122,10 @@ let's see how long Emacs takes to start up, before even loading
 =init.el=, i.e. =user-init-file=:
 
 #+begin_src emacs-lisp
 =init.el=, i.e. =user-init-file=:
 
 #+begin_src emacs-lisp
-(defvar ab--before-user-init-time (current-time)
+(defvar a/before-user-init-time (current-time)
   "Value of `current-time' when Emacs begins loading `user-init-file'.")
 (message "Loading Emacs...done (%.3fs)"
   "Value of `current-time' when Emacs begins loading `user-init-file'.")
 (message "Loading Emacs...done (%.3fs)"
-         (float-time (time-subtract ab--before-user-init-time
+         (float-time (time-subtract a/before-user-init-time
                                     before-init-time)))
 #+end_src
 
                                     before-init-time)))
 #+end_src
 
@@ -124,9 +135,9 @@ frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
 startup time as well.
 
 #+begin_src emacs-lisp
 startup time as well.
 
 #+begin_src emacs-lisp
-(defvar ab--gc-cons-threshold gc-cons-threshold)
-(defvar ab--gc-cons-percentage gc-cons-percentage)
-(defvar ab--file-name-handler-alist file-name-handler-alist)
+(defvar a/gc-cons-threshold gc-cons-threshold)
+(defvar a/gc-cons-percentage gc-cons-percentage)
+(defvar a/file-name-handler-alist file-name-handler-alist)
 (setq gc-cons-threshold (* 400 1024 1024)  ; 400 MiB
       gc-cons-percentage 0.6
       file-name-handler-alist nil
 (setq gc-cons-threshold (* 400 1024 1024)  ; 400 MiB
       gc-cons-percentage 0.6
       file-name-handler-alist nil
@@ -141,13 +152,9 @@ done initializing.
 (add-hook
  'after-init-hook
  (lambda ()
 (add-hook
  'after-init-hook
  (lambda ()
-   (let ((elapsed (float-time (time-subtract (current-time)
-                                             ab--before-user-init-time))))
-     (message "Loading %s...done (%.3fs) [after-init]"
-              user-init-file elapsed))
-   (setq gc-cons-threshold ab--gc-cons-threshold
-         gc-cons-percentage ab--gc-cons-percentage
-         file-name-handler-alist ab--file-name-handler-alist)))
+   (setq gc-cons-threshold a/gc-cons-threshold
+         gc-cons-percentage a/gc-cons-percentage
+         file-name-handler-alist a/file-name-handler-alist)))
 #+end_src
 
 Increase the number of lines kept in message logs (the =*Messages*=
 #+end_src
 
 Increase the number of lines kept in message logs (the =*Messages*=
@@ -166,6 +173,13 @@ but for now I've decided to keep them enabled. See documentation for
 ;;       '(not free-vars unresolved noruntime lexical make-local))
 #+end_src
 
 ;;       '(not free-vars unresolved noruntime lexical make-local))
 #+end_src
 
+** whoami
+
+#+begin_src emacs-lisp
+(setq user-full-name "Amin Bandali"
+      user-mail-address "amin@aminb.org")
+#+end_src
+
 ** Package management
 
 *** No =package.el=
 ** Package management
 
 *** No =package.el=
@@ -203,8 +217,23 @@ can then be managed with the help of Magit or other tools.
       user-emacs-directory (file-name-directory user-init-file))
 (add-to-list 'load-path
              (expand-file-name "lib/borg" user-emacs-directory))
       user-emacs-directory (file-name-directory user-init-file))
 (add-to-list 'load-path
              (expand-file-name "lib/borg" user-emacs-directory))
+;; Main engine start...
 (require 'borg)
 (require 'borg)
+;; Solid rocket booster ignition...
 (borg-initialize)
 (borg-initialize)
+;; We have lift off!
+
+;; (require 'borg-nix-shell)
+;; (setq borg-build-shell-command 'borg-nix-shell-build-command)
+
+(with-eval-after-load 'bind-key
+  (bind-keys
+   :package borg
+   ("C-c b A" . borg-activate)
+   ("C-c b a" . borg-assimilate)
+   ("C-c b b" . borg-build)
+   ("C-c b c" . borg-clone)
+   ("C-c b r" . borg-remove)))
 #+end_src
 
 *** =use-package=
 #+end_src
 
 *** =use-package=
@@ -219,13 +248,15 @@ and without compromising on performance.
 
 #+begin_src emacs-lisp
 (require 'use-package)
 
 #+begin_src emacs-lisp
 (require 'use-package)
-(if nil                                    ; set to t when need to debug init
+(if nil  ; set to t when need to debug init
     (setq use-package-verbose t
           use-package-expand-minimally nil
           use-package-compute-statistics t
           debug-on-error t)
   (setq use-package-verbose nil
         use-package-expand-minimally t))
     (setq use-package-verbose t
           use-package-expand-minimally nil
           use-package-compute-statistics t
           debug-on-error t)
   (setq use-package-verbose nil
         use-package-expand-minimally t))
+
+(setq use-package-always-defer t)
 #+end_src
 
 *** Epkg
 #+end_src
 
 *** Epkg
@@ -240,7 +271,15 @@ database, low-level functions for querying the database, and a
 
 #+begin_src emacs-lisp
 (use-package epkg
 
 #+begin_src emacs-lisp
 (use-package epkg
-  :defer t)
+  :bind
+  (("C-c b d" . epkg-describe-package)
+   ("C-c b p" . epkg-list-packages)
+   ("C-c b u" . epkg-update))
+  :config
+  (eval-when-compile (defvar ivy-initial-inputs-alist))
+  (with-eval-after-load 'ivy
+    (add-to-list
+     'ivy-initial-inputs-alist '(epkg-describe-package . "^") t)))
 #+end_src
 
 ** No littering in =~/.emacs.d=
 #+end_src
 
 ** No littering in =~/.emacs.d=
@@ -280,6 +319,15 @@ it it's own file. While at it, treat themes as safe.
   (setf custom-safe-themes t))
 #+end_src
 
   (setf custom-safe-themes t))
 #+end_src
 
+** Secrets file
+
+Load the secrets file if it exists, otherwise show a warning.
+
+#+begin_src emacs-lisp
+(with-demoted-errors
+    (load (no-littering-expand-etc-file-name "secrets")))
+#+end_src
+
 ** Better =$PATH= handling
 
 Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
 ** Better =$PATH= handling
 
 Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
@@ -297,6 +345,14 @@ in my shell.
   (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
 #+end_src
 
   (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
 #+end_src
 
+** COMMENT Only one custom theme at a time
+
+#+begin_src emacs-lisp
+(defadvice load-theme (before clear-previous-themes activate)
+  "Clear existing theme settings instead of layering them"
+  (mapc #'disable-theme custom-enabled-themes))
+#+end_src
+
 ** Server
 
 Start server if not already running. Alternatively, can be done by
 ** Server
 
 Start server if not already running. Alternatively, can be done by
@@ -309,9 +365,110 @@ See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.htm
 
 #+begin_src emacs-lisp
 (use-package server
 
 #+begin_src emacs-lisp
 (use-package server
+  :defer 1
   :config (or (server-running-p) (server-mode)))
 #+end_src
 
   :config (or (server-running-p) (server-mode)))
 #+end_src
 
+** COMMENT Unicode support
+
+Font stack with better unicode support, around =Ubuntu Mono= and
+=Hack=.
+
+#+begin_src emacs-lisp
+(dolist (ft (fontset-list))
+  (set-fontset-font
+   ft
+   'unicode
+   (font-spec :name "Source Code Pro" :size 14))
+  (set-fontset-font
+   ft
+   'unicode
+   (font-spec :name "DejaVu Sans Mono")
+   nil
+   'append)
+  ;; (set-fontset-font
+  ;;  ft
+  ;;  'unicode
+  ;;  (font-spec
+  ;;   :name "Symbola monospacified for DejaVu Sans Mono")
+  ;;  nil
+  ;;  'append)
+  ;; (set-fontset-font
+  ;;  ft
+  ;;  #x2115  ; ℕ
+  ;;  (font-spec :name "DejaVu Sans Mono")
+  ;;  nil
+  ;;  'append)
+  (set-fontset-font
+   ft
+   (cons ?Α ?ω)
+   (font-spec :name "DejaVu Sans Mono" :size 14)
+   nil
+   'prepend))
+#+end_src
+
+** Gentler font resizing
+
+#+begin_src emacs-lisp
+(setq text-scale-mode-step 1.05)
+#+end_src
+
+** Focus follows mouse
+
+I’d like focus to follow the mouse when I move the cursor from one
+window to the next.
+
+#+begin_src emacs-lisp
+(setq mouse-autoselect-window t)
+#+end_src
+
+Let’s define a function to conveniently disable this for certain
+buffers and/or modes.
+
+#+begin_src emacs-lisp
+(defun a/no-mouse-autoselect-window ()
+  (make-local-variable 'mouse-autoselect-window)
+  (setq mouse-autoselect-window nil))
+#+end_src
+
+** Libraries
+
+#+begin_src emacs-lisp
+(require 'cl-lib)
+(require 'subr-x)
+#+end_src
+
+** Useful utilities
+
+Convenience macro for =setq='ing multiple variables to the same value:
+
+#+begin_src emacs-lisp
+(defmacro a/setq-every (value &rest vars)
+  "Set all the variables from VARS to value VALUE."
+  (declare (indent defun) (debug t))
+  `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
+#+end_src
+
+The following process-related stuff from [[https://github.com/alezost/emacs-config][alezost's emacs-config]].
+
+#+begin_src emacs-lisp
+(defun a/start-process (program &rest args)
+  "Same as `start-process', but doesn't bother about name and buffer."
+  (let ((process-name (concat program "_process"))
+        (buffer-name  (generate-new-buffer-name
+                       (concat program "_output"))))
+    (apply #'start-process
+           process-name buffer-name program args)))
+
+(defun a/dired-start-process (program &optional args)
+  "Open current file with a PROGRAM."
+  ;; Shell command looks like this: "program [ARGS]... FILE" (ARGS can
+  ;; be nil, so remove it).
+  (apply #'a/start-process
+         program
+         (remove nil (list args (dired-get-file-for-visit)))))
+#+end_src
+
 * Core
 :PROPERTIES:
 :CUSTOM_ID: core
 * Core
 :PROPERTIES:
 :CUSTOM_ID: core
@@ -319,6 +476,26 @@ See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.htm
 
 ** Defaults
 
 
 ** Defaults
 
+*** Time and battery in mode-line
+
+Enable displaying time and battery in the mode-line, since I'm not
+using the Xfce panel anymore. Also, I don't need to see the load
+average on a regular basis, so disable that.
+
+Note: using =i3status= on sway at the moment, so disabling this.
+
+#+begin_src emacs-lisp :tangle no
+(use-package time
+  :init
+  (setq display-time-default-load-average nil)
+  :config
+  (display-time-mode))
+
+(use-package battery
+  :config
+  (display-battery-mode))
+#+end_src
+
 *** Smaller fringe
 
 Might want to set the fringe to a smaller value, especially if using
 *** Smaller fringe
 
 Might want to set the fringe to a smaller value, especially if using
@@ -326,6 +503,7 @@ EXWM. I'm fine with the default for now.
 
 #+begin_src emacs-lisp
 ;; (fringe-mode '(3 . 1))
 
 #+begin_src emacs-lisp
 ;; (fringe-mode '(3 . 1))
+(fringe-mode nil)
 #+end_src
 
 *** Disable disabled commands
 #+end_src
 
 *** Disable disabled commands
@@ -424,7 +602,112 @@ variable.
 
 #+begin_src emacs-lisp
 (setq backup-by-copying t
 
 #+begin_src emacs-lisp
 (setq backup-by-copying t
-      version-control t)
+      version-control t
+      delete-old-versions t)
+#+end_src
+
+*** Auto revert
+
+Enable automatic reloading of changed buffers and files.
+
+#+begin_src emacs-lisp
+(global-auto-revert-mode 1)
+(setq auto-revert-verbose nil
+      global-auto-revert-non-file-buffers nil)
+#+end_src
+
+*** Always use space for indentation
+
+#+begin_src emacs-lisp
+(setq-default
+ indent-tabs-mode nil
+ require-final-newline t
+ tab-width 4)
+#+end_src
+
+*** Winner mode
+
+Enable =winner-mode=.
+
+#+begin_src emacs-lisp
+(winner-mode 1)
+#+end_src
+
+*** Don’t display =*compilation*= on success
+
+Based on https://stackoverflow.com/a/17788551, with changes to use
+=cl-letf= instead of the now obsolete =flet=.
+
+#+begin_src emacs-lisp
+(with-eval-after-load 'compile
+  (defun a/compilation-finish-function (buffer outstr)
+    (unless (string-match "finished" outstr)
+      (switch-to-buffer-other-window buffer))
+    t)
+
+  (setq compilation-finish-functions #'a/compilation-finish-function)
+
+  (require 'cl-macs)
+
+  (defadvice compilation-start
+      (around inhibit-display
+              (command &optional mode name-function highlight-regexp))
+    (if (not (string-match "^\\(find\\|grep\\)" command))
+        (cl-letf (((symbol-function 'display-buffer) #'ignore))
+          (save-window-excursion ad-do-it))
+      ad-do-it))
+  (ad-activate 'compilation-start))
+#+end_src
+
+*** Search for non-ASCII characters
+
+I’d like non-ASCII characters such as ‘’“”«»‹›áⓐ𝒶 to be selected when
+I search for their ASCII counterpart.  Shoutout to [[http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html][endlessparentheses]]
+for this.
+
+#+begin_src emacs-lisp
+(setq search-default-mode #'char-fold-to-regexp)
+
+;; uncomment to extend this behaviour to query-replace
+;; (setq replace-char-fold t)
+#+end_src
+
+*** Cursor shape
+
+#+begin_src emacs-lisp
+(setq-default cursor-type 'bar)
+#+end_src
+
+*** Allow scrolling in Isearch
+
+#+begin_src emacs-lisp
+(setq isearch-allow-scroll t)
+#+end_src
+
+** Bindings
+
+#+begin_src emacs-lisp
+(bind-keys
+ ("C-c a i" . ielm)
+
+ ("C-c e b" . eval-buffer)
+ ("C-c e r" . eval-region)
+
+ ("C-c F m" . make-frame-command)
+ ("C-c F d" . delete-frame)
+ ("C-c F D" . delete-other-frames)
+
+ ("C-c o"   . other-window)
+
+ ("C-c Q"   . save-buffers-kill-terminal)
+
+ ("C-S-h C" . describe-char)
+ ("C-S-h F" . describe-face)
+
+ ("C-x K"   . kill-this-buffer)
+
+ ("s-p"     . beginning-of-buffer)
+ ("s-n"     . end-of-buffer))
 #+end_src
 
 ** Packages
 #+end_src
 
 ** Packages
@@ -443,48 +726,129 @@ customizing it.
   (auto-compile-on-load-mode)
   (auto-compile-on-save-mode)
   (setq auto-compile-display-buffer               nil
   (auto-compile-on-load-mode)
   (auto-compile-on-save-mode)
   (setq auto-compile-display-buffer               nil
-       auto-compile-mode-line-counter            t
-       auto-compile-source-recreate-deletes-dest t
-       auto-compile-toggle-deletes-nonlib-dest   t
-       auto-compile-update-autoloads             t)
+        auto-compile-mode-line-counter            t
+        auto-compile-source-recreate-deletes-dest t
+        auto-compile-toggle-deletes-nonlib-dest   t
+        auto-compile-update-autoloads             t)
   (add-hook 'auto-compile-inhibit-compile-hook
             'auto-compile-inhibit-compile-detached-git-head))
 #+end_src
 
   (add-hook 'auto-compile-inhibit-compile-hook
             'auto-compile-inhibit-compile-detached-git-head))
 #+end_src
 
-*** TODO [[https://github.com/Kungsgeten/ryo-modal][ryo-modal]]
+*** [[https://orgmode.org/][Org]]
 
 #+begin_quote
 
 #+begin_quote
-Roll your own modal mode
+Org mode is for keeping notes, maintaining TODO lists, planning
+projects, and authoring documents with a fast and effective plain-text
+system.
 #+end_quote
 
 #+end_quote
 
-*** [[https://github.com/ch11ng/exwm][EXWM]] (window manager)
+In short, my favourite way of life.
 
 #+begin_src emacs-lisp
 
 #+begin_src emacs-lisp
-(use-package exwm
-  :demand t
+(use-package org
+  :defer 1
+  :config
+  (setq org-src-tab-acts-natively t
+        org-src-preserve-indentation nil
+        org-edit-src-content-indentation 0
+        org-email-link-description-format "Email %c: %s" ; %.30s
+        org-highlight-latex-and-related '(entities)
+        org-use-speed-commands t
+        org-startup-folded 'content
+        org-catch-invisible-edits 'show-and-error
+        org-log-done 'time)
+  (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t)
+  (font-lock-add-keywords
+   'org-mode
+   '(("[ \t]*\\(#\\+\\(BEGIN\\|END\\|begin\\|end\\)_\\(\\S-+\\)\\)[ \t]*\\([^\n:]*\\)"
+      (1 '(:foreground "#5a5b5a" :background "#292b2b") t) ; directive
+      (3 '(:foreground "#81a2be" :background "#292b2b") t) ; kind
+      (4 '(:foreground "#c5c8c6") t))) ; title
+   t)
+  :bind (:map org-mode-map ("M-L" . org-insert-last-stored-link))
+  :hook ((org-mode . org-indent-mode)
+         (org-mode . auto-fill-mode)
+         (org-mode . flyspell-mode))
+  :custom
+  (org-latex-packages-alist '(("" "listings") ("" "color")))
+  :custom-face
+  '(org-block-begin-line ((t (:foreground "#5a5b5a" :background "#1d1f21"))))
+  '(org-block ((t (:background "#1d1f21"))))
+  '(org-latex-and-related ((t (:foreground "#b294bb")))))
+
+(use-package ox-latex
+  :after ox
   :config
   :config
-  (require 'exwm-config)
-  (exwm-config-default)
-  (require 'exwm-systemtray)
-  (exwm-systemtray-enable)
-  (require 'exwm-randr)
-  (exwm-randr-enable))
+  (setq org-latex-listings 'listings
+        ;; org-latex-prefer-user-labels t
+        )
+  (add-to-list 'org-latex-classes
+               '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
+                 ("\\section{%s}"       . "\\section*{%s}")
+                 ("\\subsection{%s}"    . "\\subsection*{%s}")
+                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
+                 ("\\paragraph{%s}"     . "\\paragraph*{%s}")
+                 ("\\subparagraph{%s}"  . "\\subparagraph*{%s}"))
+               t))
+
+(use-package ox-beamer
+  :demand
+  :after ox)
 #+end_src
 
 #+end_src
 
-*** [[https://orgmode.org/][Org mode]]
-
-#+begin_quote
-Org mode is for keeping notes, maintaining TODO lists, planning
-projects, and authoring documents with a fast and effective plain-text
-system.
-#+end_quote
+**** asynchronous tangle
 
 
-In short, my favourite way of life.
+=a/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
+d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
+file.
 
 #+begin_src emacs-lisp
 
 #+begin_src emacs-lisp
-(setq org-src-tab-acts-natively t
-      org-src-preserve-indentation nil
-      org-edit-src-content-indentation 0)
+(with-eval-after-load 'org
+  (defvar a/show-async-tangle-results nil
+    "Keep *emacs* async buffers around for later inspection.")
+
+  (defvar a/show-async-tangle-time nil
+    "Show the time spent tangling the file.")
+
+  (defvar a/async-tangle-post-compile "make ti"
+    "If non-nil, pass to `compile' after successful tangle.")
+
+  (defvar a/async-tangle-byte-recompile nil
+    "If non-nil, byte-recompile the file on successful tangle.")
+
+  (defun a/async-babel-tangle ()
+    "Tangle org file asynchronously."
+    (interactive)
+    (let* ((file-tangle-start-time (current-time))
+           (file (buffer-file-name))
+           (file-nodir (file-name-nondirectory file))
+           ;; (async-quiet-switch "-q")
+           (file-noext (file-name-sans-extension file)))
+      (async-start
+       `(lambda ()
+          (require 'org)
+          (org-babel-tangle-file ,file))
+       (unless a/show-async-tangle-results
+         `(lambda (result)
+            (if result
+                (progn
+                  ;; (setq byte-compile-warnings '(not noruntime unresolved))
+                  (message "Tangled %s%s"
+                           ,file-nodir
+                           (if a/show-async-tangle-time
+                               (format " (%.3fs)"
+                                       (float-time (time-subtract (current-time)
+                                                                  ',file-tangle-start-time)))
+                             ""))
+                  (when a/async-tangle-post-compile
+                    (compile a/async-tangle-post-compile))
+                  (when a/async-tangle-byte-recompile
+                    (byte-recompile-file (concat ,file-noext ".el"))))
+              (message "Tangling %s failed" ,file-nodir))))))))
+
+(add-to-list
+ 'safe-local-variable-values
+ '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
 #+end_src
 
 *** [[https://magit.vc/][Magit]]
 #+end_src
 
 *** [[https://magit.vc/][Magit]]
@@ -497,15 +861,46 @@ Not just how I do git, but /the/ way to do git.
 
 #+begin_src emacs-lisp
 (use-package magit
 
 #+begin_src emacs-lisp
 (use-package magit
-  :defer t
-  :bind (("s-g"     . magit-status)
-        ("C-x g"   . magit-status)
-         ("C-x M-g" . magit-dispatch-popup))
+  :defer 1
+  :bind (("C-x g" . magit-status)
+         ("s-g s" . magit-status)
+         ("s-g l" . magit-log-buffer-file))
   :config
   (magit-add-section-hook 'magit-status-sections-hook
                           'magit-insert-modules
                           'magit-insert-stashes
   :config
   (magit-add-section-hook 'magit-status-sections-hook
                           'magit-insert-modules
                           'magit-insert-stashes
-                          'append))
+                          'append)
+  (setq
+   magit-repository-directories '(("~/.emacs.d/" . 0)
+                                  ("~/src/git/" . 1)))
+  (nconc magit-section-initial-visibility-alist
+         '(([unpulled status] . show)
+           ([unpushed status] . show)))
+  :custom-face (magit-diff-file-heading ((t (:weight normal)))))
+#+end_src
+
+*** recentf
+
+Recently opened files.
+
+#+begin_src emacs-lisp
+(use-package recentf
+  :defer 0.5
+  :config
+  (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
+  (setq recentf-max-saved-items 40))
+#+end_src
+
+*** smex
+
+#+begin_quote
+A smart M-x enhancement for Emacs.
+#+end_quote
+
+Mostly because =counsel= needs it to remember history.
+
+#+begin_src emacs-lisp
+(use-package smex)
 #+end_src
 
 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
 #+end_src
 
 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
@@ -521,132 +916,1441 @@ There's no way I could top that, so I won't attempt to.
 
 #+begin_src emacs-lisp
 (use-package ivy
 
 #+begin_src emacs-lisp
 (use-package ivy
+  :defer 1
   :bind
   (:map ivy-minibuffer-map
   :bind
   (:map ivy-minibuffer-map
-        ([escape] . keyboard-escape-quit)
-        ("C-j"    . ivy-next-line)
-        ("C-k"    . ivy-previous-line)
-        ([S-up]   . ivy-previous-history-element)
-        ([S-down] . ivy-next-history-element)
-        ("DEL"    . ivy-backward-delete-char))
+   ([escape] . keyboard-escape-quit)
+   ([S-up]   . ivy-previous-history-element)
+   ([S-down] . ivy-next-history-element)
+   ("DEL"    . ivy-backward-delete-char))
   :config
   :config
-  (ivy-mode 1))
+  (setq ivy-wrap t)
+  (ivy-mode 1)
+  ;; :custom-face
+  ;; (ivy-minibuffer-match-face-2 ((t (:background "#e99ce8" :weight semi-bold))))
+  ;; (ivy-minibuffer-match-face-3 ((t (:background "#bbbbff" :weight semi-bold))))
+  ;; (ivy-minibuffer-match-face-4 ((t (:background "#ffbbff" :weight semi-bold))))
+)
 #+end_src
 
 **** Swiper
 
 #+begin_src emacs-lisp
 (use-package swiper
 #+end_src
 
 **** Swiper
 
 #+begin_src emacs-lisp
 (use-package swiper
-  :bind (([remap isearch-forward]  . swiper)
-        ([remap isearch-backward] . swiper)))
+  :after ivy
+  :bind (("C-s"   . swiper)
+         ("C-r"   . swiper)
+         ("C-S-s" . isearch-forward)))
 #+end_src
 
 **** Counsel
 
 #+begin_src emacs-lisp
 (use-package counsel
 #+end_src
 
 **** Counsel
 
 #+begin_src emacs-lisp
 (use-package counsel
+  :defer 1
+  :after ivy
   :bind (([remap execute-extended-command] . counsel-M-x)
   :bind (([remap execute-extended-command] . counsel-M-x)
-         ([remap find-file] . counsel-find-file)
-         ("s-r"     . counsel-recentf)
+         ([remap find-file]                . counsel-find-file)
+         ("s-r"                            . counsel-recentf)
+         ("C-c x"                          . counsel-M-x)
+         ("C-c f ."                        . counsel-find-file)
          :map minibuffer-local-map
          :map minibuffer-local-map
-        ("C-r" . counsel-minibuffer-history))
+         ("C-r" . counsel-minibuffer-history))
   :config
   (counsel-mode 1)
   (defalias 'locate #'counsel-locate))
 #+end_src
 
   :config
   (counsel-mode 1)
   (defalias 'locate #'counsel-locate))
 #+end_src
 
-* Borg's =layer/essentials=
+*** eshell
+
+#+begin_src emacs-lisp
+(use-package eshell
+  :defer 1
+  :commands eshell
+  :bind ("C-c a s e" . eshell)
+  :config
+  (eval-when-compile (defvar eshell-prompt-regexp))
+  (defun a/eshell-quit-or-delete-char (arg)
+    (interactive "p")
+    (if (and (eolp) (looking-back eshell-prompt-regexp nil))
+        (eshell-life-is-too-much)
+      (delete-char arg)))
+
+  (defun a/eshell-clear ()
+    (interactive)
+    (let ((inhibit-read-only t))
+      (erase-buffer))
+    (eshell-send-input))
+
+  (defun a/eshell-setup ()
+    (make-local-variable 'company-idle-delay)
+    (defvar company-idle-delay)
+    (setq company-idle-delay nil)
+    (bind-keys :map eshell-mode-map
+               ("C-d"   . a/eshell-quit-or-delete-char)
+               ("C-S-l" . a/eshell-clear)
+               ("M-r"   . counsel-esh-history)
+               ([tab]   . company-complete)))
+
+  :hook (eshell-mode . a/eshell-setup)
+  :custom
+  (eshell-hist-ignoredups t)
+  (eshell-input-filter 'eshell-input-filter-initial-space))
+#+end_src
+
+*** Ibuffer
+
+#+begin_src emacs-lisp
+(use-package ibuffer
+  :bind
+  (("C-x C-b" . ibuffer-other-window)
+   :map ibuffer-mode-map
+   ("P"   . ibuffer-backward-filter-group)
+   ("N"   . ibuffer-forward-filter-group)
+   ("M-p" . ibuffer-do-print)
+   ("M-n" . ibuffer-do-shell-command-pipe-replace))
+  :config
+  ;; Use human readable Size column instead of original one
+  (define-ibuffer-column size-h
+    (:name "Size" :inline t)
+    (cond
+     ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
+     ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
+     ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
+     (t (format "%8d" (buffer-size)))))
+  :custom
+  (ibuffer-saved-filter-groups
+   '(("default"
+      ("dired" (mode . dired-mode))
+      ("org"   (mode . org-mode))
+      ("gnus"
+       (or
+        (mode . gnus-group-mode)
+        (mode . gnus-summary-mode)
+        (mode . gnus-article-mode)
+        ;; not really, but...
+        (mode . message-mode)))
+      ("web"
+       (or
+        (mode . web-mode)
+        (mode . css-mode)
+        (mode . scss-mode)
+        (mode . js2-mode)))
+      ("shell"
+       (or
+        (mode . eshell-mode)
+        (mode . shell-mode)
+        (mode . term-mode)))
+      ("programming"
+       (or
+        (mode . python-mode)
+        (mode . c-mode)
+        (mode . c++-mode)
+        (mode . emacs-lisp-mode)
+        (mode . scheme-mode)
+        (mode . haskell-mode)
+        (mode . lean-mode)))
+      ("emacs"
+       (or
+        (name . "^\\*scratch\\*$")
+        (name . "^\\*Messages\\*$"))))))
+  (ibuffer-formats
+   '((mark modified read-only locked " "
+           (name 18 18 :left :elide)
+           " "
+           (size-h 9 -1 :right)
+           " "
+           (mode 16 16 :left :elide)
+           " " filename-and-process)
+     (mark " "
+           (name 16 -1)
+           " " filename)))
+  :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
+#+end_src
+
+*** Outline
+
+#+begin_src emacs-lisp
+(use-package outline
+  :hook (prog-mode . outline-minor-mode)
+  :bind
+  (:map
+   outline-minor-mode-map
+   ("<s-tab>"  . outline-toggle-children)
+   ("M-p"      . outline-previous-visible-heading)
+   ("M-n"      . outline-next-visible-heading)
+   :prefix-map a/outline-prefix-map
+   :prefix "s-o"
+   ("TAB" . outline-toggle-children)
+   ("a"   . outline-hide-body)
+   ("H"   . outline-hide-body)
+   ("S"   . outline-show-all)
+   ("h"   . outline-hide-subtree)
+   ("s"   . outline-show-subtree)))
+#+end_src
+
+*** Dired
+
+#+begin_src emacs-lisp
+(use-package ls-lisp
+  :custom (ls-lisp-dirs-first t))
+
+(use-package dired
+  :config
+  (setq dired-listing-switches "-alh"
+        ls-lisp-use-insert-directory-program nil)
+
+  ;; easily diff 2 marked files
+  ;; https://oremacs.com/2017/03/18/dired-ediff/
+  (defun dired-ediff-files ()
+    (interactive)
+    (require 'dired-aux)
+    (defvar ediff-after-quit-hook-internal)
+    (let ((files (dired-get-marked-files))
+          (wnd (current-window-configuration)))
+      (if (<= (length files) 2)
+          (let ((file1 (car files))
+                (file2 (if (cdr files)
+                           (cadr files)
+                         (read-file-name
+                          "file: "
+                          (dired-dwim-target-directory)))))
+            (if (file-newer-than-file-p file1 file2)
+                (ediff-files file2 file1)
+              (ediff-files file1 file2))
+            (add-hook 'ediff-after-quit-hook-internal
+                      (lambda ()
+                        (setq ediff-after-quit-hook-internal nil)
+                        (set-window-configuration wnd))))
+        (error "no more than 2 files should be marked"))))
+  :bind (:map dired-mode-map
+              ("b"  . dired-up-directory)
+              ("e"  . dired-ediff-files)
+              ("E"  . dired-toggle-read-only)
+              ("\\" . dired-hide-details-mode)
+              ("z"  . (lambda ()
+                        (interactive)
+                        (a/dired-start-process "zathura"))))
+  :hook (dired-mode . dired-hide-details-mode))
+#+end_src
+
+*** Help
+
+#+begin_src emacs-lisp
+(use-package help
+  :config
+  (temp-buffer-resize-mode)
+  (setq help-window-select t))
+#+end_src
+
+*** Tramp
+
+#+begin_src emacs-lisp
+(use-package tramp
+  :config
+  (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
+  (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
+  (add-to-list 'tramp-default-proxies-alist
+               (list (regexp-quote (system-name)) nil nil)))
+#+end_src
 
 
-TODO: break this giant source block down into individual org sections.
+*** Dash
 
 #+begin_src emacs-lisp
 (use-package dash
   :config (dash-enable-font-lock))
 
 #+begin_src emacs-lisp
 (use-package dash
   :config (dash-enable-font-lock))
+#+end_src
+
+* Editing
+:PROPERTIES:
+:CUSTOM_ID: editing
+:END:
+
+** =diff-hl=
 
 
+Highlight uncommitted changes in the left fringe.
+
+#+begin_src emacs-lisp
 (use-package diff-hl
   :config
   (setq diff-hl-draw-borders nil)
   (global-diff-hl-mode)
 (use-package diff-hl
   :config
   (setq diff-hl-draw-borders nil)
   (global-diff-hl-mode)
-  (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
+  :hook (magit-post-refresh . diff-hl-magit-post-refresh))
+#+end_src
 
 
-(use-package dired
-  :defer t
-  :config (setq dired-listing-switches "-alh"))
+** ElDoc
 
 
+Display Lisp objects at point in the echo area.
+
+#+begin_src emacs-lisp
 (use-package eldoc
   :when (version< "25" emacs-version)
   :config (global-eldoc-mode))
 (use-package eldoc
   :when (version< "25" emacs-version)
   :config (global-eldoc-mode))
+#+end_src
 
 
-(use-package help
-  :defer t
-  :config (temp-buffer-resize-mode))
-
-(progn ;    `isearch'
-  (setq isearch-allow-scroll t))
-
-(use-package lisp-mode
-  :config
-  (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
-  (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
-  (defun indent-spaces-mode ()
-    (setq indent-tabs-mode nil))
-  (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
+** paren
 
 
-(use-package man
-  :defer t
-  :config (setq Man-width 80))
+Highlight matching parens.
 
 
+#+begin_src emacs-lisp
 (use-package paren
 (use-package paren
+  :demand
   :config (show-paren-mode))
   :config (show-paren-mode))
+#+end_src
 
 
-(use-package prog-mode
-  :config (global-prettify-symbols-mode)
-  (defun indicate-buffer-boundaries-left ()
-    (setq indicate-buffer-boundaries 'left))
-  (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
+** simple (for column numbers)
 
 
-(use-package recentf
-  :demand t
-  :config (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:"))
+#+begin_src emacs-lisp
+(use-package simple
+  :config (column-number-mode))
+#+end_src
+
+** =savehist=
+
+Save minibuffer history.
 
 
+#+begin_src emacs-lisp
 (use-package savehist
   :config (savehist-mode))
 (use-package savehist
   :config (savehist-mode))
+#+end_src
+
+** =saveplace=
+
+Automatically save place in each file.
 
 
+#+begin_src emacs-lisp
 (use-package saveplace
   :when (version< "25" emacs-version)
   :config (save-place-mode))
 (use-package saveplace
   :when (version< "25" emacs-version)
   :config (save-place-mode))
+#+end_src
 
 
-(use-package simple
-  :config (column-number-mode))
+** =prog-mode=
 
 
-(progn ;    `text-mode'
-  (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left))
+#+begin_src emacs-lisp
+(use-package prog-mode
+  :config (global-prettify-symbols-mode)
+  (defun indicate-buffer-boundaries-left ()
+    (setq indicate-buffer-boundaries 'left))
+  (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
+#+end_src
 
 
-(use-package tramp
-  :defer t
-  :config
-  (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
-  (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
-  (add-to-list 'tramp-default-proxies-alist
-               (list (regexp-quote (system-name)) nil nil)))
+** =text-mode=
 
 
-(use-package undo-tree
-  :config
-  (global-undo-tree-mode)
-  (setq undo-tree-mode-lighter ""))
+#+begin_src emacs-lisp
+(use-package text-mode
+  :hook ((text-mode . indicate-buffer-boundaries-left)
+         (text-mode . abbrev-mode)))
 #+end_src
 
 #+end_src
 
-* Post initialization
-:PROPERTIES:
-:CUSTOM_ID: post-initialization
-:END:
+** Company
 
 
-Display how long it took to load the init file.
+#+begin_src emacs-lisp
+(use-package company
+  :defer 1
+  :bind
+  (:map company-active-map
+        ([tab]    . company-complete-common-or-cycle)
+        ([escape] . company-abort))
+  :custom
+  (company-minimum-prefix-length 1)
+  (company-selection-wrap-around t)
+  (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
+  (company-dabbrev-downcase nil)
+  (company-dabbrev-ignore-case nil)
+  :config
+  (global-company-mode t))
+#+end_src
+
+** Flycheck
+
+#+begin_src emacs-lisp
+(use-package flycheck
+  :defer 3
+  :hook (prog-mode . flycheck-mode)
+  :bind
+  (:map flycheck-mode-map
+        ("M-P" . flycheck-previous-error)
+        ("M-N" . flycheck-next-error))
+  :config
+  ;; Use the load-path from running Emacs when checking elisp files
+  (setq flycheck-emacs-lisp-load-path 'inherit)
+
+  ;; Only flycheck when I actually save the buffer
+  (setq flycheck-check-syntax-automatically '(mode-enabled save)))
+
+;; http://endlessparentheses.com/ispell-and-apostrophes.html
+(use-package ispell
+  :defer 3
+  :config
+  ;; ’ can be part of a word
+  (setq ispell-local-dictionary-alist
+        `((nil "[[:alpha:]]" "[^[:alpha:]]"
+               "['\x2019]" nil ("-B") nil utf-8)))
+  ;; don't send ’ to the subprocess
+  (defun endless/replace-apostrophe (args)
+    (cons (replace-regexp-in-string
+           "’" "'" (car args))
+          (cdr args)))
+  (advice-add #'ispell-send-string :filter-args
+              #'endless/replace-apostrophe)
+
+  ;; convert ' back to ’ from the subprocess
+  (defun endless/replace-quote (args)
+    (if (not (derived-mode-p 'org-mode))
+        args
+      (cons (replace-regexp-in-string
+             "'" "’" (car args))
+            (cdr args))))
+  (advice-add #'ispell-parse-output :filter-args
+              #'endless/replace-quote))
+#+end_src
+
+* Programming modes
+:PROPERTIES:
+:CUSTOM_ID: programming-modes
+:END:
+
+** Lisp
+
+#+begin_src emacs-lisp
+(use-package lisp-mode
+  :config
+  (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
+  (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
+  (defun indent-spaces-mode ()
+    (setq indent-tabs-mode nil))
+  (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
+#+end_src
+
+** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
+
+#+begin_src emacs-lisp
+(use-package alloy-mode
+  :config (setq alloy-basic-offset 2))
+#+end_src
+
+** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
+
+#+begin_src emacs-lisp
+(use-package proof-site  ; Proof General
+  :load-path "lib/proof-site/generic/")
+#+end_src
+
+** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
+
+#+begin_src emacs-lisp
+(eval-when-compile (defvar lean-mode-map))
+(use-package lean-mode
+  :defer 1
+  :bind (:map lean-mode-map
+              ("S-SPC" . company-complete))
+  :config
+  (require 'lean-input)
+  (setq default-input-method "Lean"
+        lean-input-tweak-all '(lean-input-compose
+                               (lean-input-prepend "/")
+                               (lean-input-nonempty))
+        lean-input-user-translations '(("/" "/")))
+  (lean-input-setup))
+  #+end_src
+
+** Haskell
+
+*** [[https://github.com/haskell/haskell-mode][haskell-mode]]
+
+#+begin_src emacs-lisp
+(use-package haskell-mode
+  :config
+  (setq haskell-indentation-layout-offset 4
+        haskell-indentation-left-offset 4
+        flycheck-checker 'haskell-hlint
+        flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
+#+end_src
+
+*** [[https://github.com/jyp/dante][dante]]
+
+#+begin_src emacs-lisp
+(use-package dante
+  :after haskell-mode
+  :commands dante-mode
+  :hook (haskell-mode . dante-mode))
+#+end_src
+
+*** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
+
+Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
+executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
+
+#+begin_src emacs-lisp
+(use-package hlint-refactor
+  :after haskell-mode
+  :bind (:map hlint-refactor-mode-map
+              ("C-c l b" . hlint-refactor-refactor-buffer)
+              ("C-c l r" . hlint-refactor-refactor-at-point))
+  :hook (haskell-mode . hlint-refactor-mode))
+#+end_src
+
+*** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
+
+#+begin_src emacs-lisp
+(use-package flycheck-haskell
+  :after haskell-mode)
+#+end_src
+
+*** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
+:PROPERTIES:
+:header-args+: :tangle lisp/hs-lint.el :mkdirp yes
+:END:
+
+Currently using =flycheck-haskell= with the =haskell-hlint= checker
+instead.
+
+#+begin_src emacs-lisp :tangle no
+;;; hs-lint.el --- minor mode for HLint code checking
+
+;; Copyright 2009 (C) Alex Ott
+;;
+;; Author: Alex Ott <alexott@gmail.com>
+;; Keywords: haskell, lint, HLint
+;; Requirements:
+;; Status: distributed under terms of GPL2 or above
+
+;; Typical message from HLint looks like:
+;;
+;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
+;; Found:
+;;   count1 p l = length (filter p l)
+;; Why not:
+;;   count1 p = length . filter p
+
+
+(require 'compile)
+
+(defgroup hs-lint nil
+  "Run HLint as inferior of Emacs, parse error messages."
+  :group 'tools
+  :group 'haskell)
+
+(defcustom hs-lint-command "hlint"
+  "The default hs-lint command for \\[hlint]."
+  :type 'string
+  :group 'hs-lint)
+
+(defcustom hs-lint-save-files t
+  "Save modified files when run HLint or no (ask user)"
+  :type 'boolean
+  :group 'hs-lint)
+
+(defcustom hs-lint-replace-with-suggestions nil
+  "Replace user's code with suggested replacements"
+  :type 'boolean
+  :group 'hs-lint)
+
+(defcustom hs-lint-replace-without-ask nil
+  "Replace user's code with suggested replacements automatically"
+  :type 'boolean
+  :group 'hs-lint)
+
+(defun hs-lint-process-setup ()
+  "Setup compilation variables and buffer for `hlint'."
+  (run-hooks 'hs-lint-setup-hook))
+
+;; regex for replace suggestions
+;;
+;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
+;; Found:
+;; \s +\(.*\)
+;; Why not:
+;; \s +\(.*\)
+
+(defvar hs-lint-regex
+  "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
+  "Regex for HLint messages")
+
+(defun make-short-string (str maxlen)
+  (if (< (length str) maxlen)
+      str
+    (concat (substring str 0 (- maxlen 3)) "...")))
+
+(defun hs-lint-replace-suggestions ()
+  "Perform actual replacement of suggestions"
+  (goto-char (point-min))
+  (while (re-search-forward hs-lint-regex nil t)
+    (let* ((fname (match-string 1))
+          (fline (string-to-number (match-string 2)))
+          (old-code (match-string 4))
+          (new-code (match-string 5))
+          (msg (concat "Replace '" (make-short-string old-code 30)
+                       "' with '" (make-short-string new-code 30) "'"))
+          (bline 0)
+          (eline 0)
+          (spos 0)
+          (new-old-code ""))
+      (save-excursion
+        (switch-to-buffer (get-file-buffer fname))
+        (goto-char (point-min))
+        (forward-line (1- fline))
+        (beginning-of-line)
+        (setf bline (point))
+        (when (or hs-lint-replace-without-ask
+                  (yes-or-no-p msg))
+          (end-of-line)
+          (setf eline (point))
+          (beginning-of-line)
+          (setf old-code (regexp-quote old-code))
+          (while (string-match "\\\\ " old-code spos)
+            (setf new-old-code (concat new-old-code
+                                 (substring old-code spos (match-beginning 0))
+                                 "\\ *"))
+            (setf spos (match-end 0)))
+          (setf new-old-code (concat new-old-code (substring old-code spos)))
+          (remove-text-properties bline eline '(composition nil))
+          (when (re-search-forward new-old-code eline t)
+            (replace-match new-code nil t)))))))
+
+(defun hs-lint-finish-hook (buf msg)
+  "Function, that is executed at the end of HLint execution"
+  (if hs-lint-replace-with-suggestions
+      (hs-lint-replace-suggestions)
+      (next-error 1 t)))
+
+(define-compilation-mode hs-lint-mode "HLint"
+  "Mode for check Haskell source code."
+  (set (make-local-variable 'compilation-process-setup-function)
+       'hs-lint-process-setup)
+  (set (make-local-variable 'compilation-disable-input) t)
+  (set (make-local-variable 'compilation-scroll-output) nil)
+  (set (make-local-variable 'compilation-finish-functions)
+       (list 'hs-lint-finish-hook))
+  )
+
+(defun hs-lint ()
+  "Run HLint for current buffer with haskell source"
+  (interactive)
+  (save-some-buffers hs-lint-save-files)
+  (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
+                     'hs-lint-mode))
+
+(provide 'hs-lint)
+;;; hs-lint.el ends here
+#+end_src
+
+#+begin_src emacs-lisp :tangle no
+(use-package hs-lint
+  :load-path "lisp/"
+  :bind (:map haskell-mode-map
+              ("C-c l l" . hs-lint)))
+#+end_src
+
+** Web
+
+*** SGML and HTML
+
+#+begin_src emacs-lisp
+(use-package sgml-mode
+  :config
+  (setq sgml-basic-offset 2))
+#+end_src
+
+*** CSS and SCSS
+
+#+begin_src emacs-lisp
+(use-package css-mode
+  :config
+  (setq css-indent-offset 2))
+#+end_src
+
+*** Web mode
+
+#+begin_src emacs-lisp
+(use-package web-mode
+  :mode "\\.html\\'"
+  :config
+  (a/setq-every 2
+    web-mode-code-indent-offset
+    web-mode-css-indent-offset
+    web-mode-markup-indent-offset))
+#+end_src
+
+*** Emmet mode
+
+#+begin_src emacs-lisp
+(use-package emmet-mode
+  :after (:any web-mode css-mode sgml-mode)
+  :bind* (("C-)" . emmet-next-edit-point)
+          ("C-(" . emmet-prev-edit-point))
+  :config
+  (unbind-key "C-j" emmet-mode-keymap)
+  (setq emmet-move-cursor-between-quotes t)
+  :hook (web-mode css-mode html-mode sgml-mode))
+#+end_src
+
+** COMMENT Java
+
+*** meghanada
+
+#+begin_src emacs-lisp
+(use-package meghanada
+  :bind
+  (:map meghanada-mode-map
+        (("C-M-o" . meghanada-optimize-import)
+         ("C-M-t" . meghanada-import-all)))
+  :hook (java-mode . meghanada-mode))
+#+end_src
+
+*** lsp-java
+
+#+begin_comment
+dependencies:
+
+ace-window
+avy
+bui
+company-lsp
+dap-mode
+lsp-java
+lsp-mode
+lsp-ui
+pfuture
+tree-mode
+treemacs
+#+end_comment
+
+#+begin_src emacs-lisp
+(use-package treemacs
+  :config (setq treemacs-never-persist t))
+
+(use-package yasnippet
+  :config
+  ;; (yas-global-mode)
+  )
+
+(use-package lsp-mode
+  :init (setq lsp-eldoc-render-all nil
+              lsp-highlight-symbol-at-point nil)
+  )
+
+(use-package hydra)
+
+(use-package company-lsp
+  :after  company
+  :config
+  (setq company-lsp-cache-candidates t
+        company-lsp-async t))
+
+(use-package lsp-ui
+  :config
+  (setq lsp-ui-sideline-update-mode 'point))
+
+(use-package lsp-java
+  :config
+  (add-hook 'java-mode-hook
+           (lambda ()
+             (setq-local company-backends (list 'company-lsp))))
+
+  (add-hook 'java-mode-hook 'lsp-java-enable)
+  (add-hook 'java-mode-hook 'flycheck-mode)
+  (add-hook 'java-mode-hook 'company-mode)
+  (add-hook 'java-mode-hook 'lsp-ui-mode))
+
+(use-package dap-mode
+  :after lsp-mode
+  :config
+  (dap-mode t)
+  (dap-ui-mode t))
+
+(use-package dap-java
+  :after (lsp-java))
+
+(use-package lsp-java-treemacs
+  :after (treemacs))
+#+end_src
+
+** geiser
+
+#+begin_src emacs-lisp
+(use-package geiser)
+
+(use-package geiser-guile
+  :config
+  (setq geiser-guile-load-path "~/src/git/guix"))
+#+end_src
+
+** guix
+
+#+begin_src emacs-lisp
+(use-package guix
+  :load-path "lib/guix/elisp")
+#+end_src
+
+* Emacs enhancements
+:PROPERTIES:
+:CUSTOM_ID: emacs-enhancements
+:END:
+
+** man
+
+#+begin_src emacs-lisp
+(use-package man
+  :config (setq Man-width 80))
+#+end_src
+
+** [[https://github.com/justbur/emacs-which-key][which-key]]
+
+#+begin_quote
+Emacs package that displays available keybindings in popup
+#+end_quote
+
+#+begin_src emacs-lisp
+(use-package which-key
+  :defer 1
+  :config
+  (which-key-add-key-based-replacements
+    ;; prefixes for global prefixes and minor modes
+    "C-c @"   "outline"
+    "C-c !"   "flycheck"
+    "C-c 8"   "typo"
+    "C-c 8 -" "typo/dashes"
+    "C-c 8 <" "typo/left-brackets"
+    "C-c 8 >" "typo/right-brackets"
+    "C-x 8"   "unicode"
+    "C-x a"   "abbrev/expand"
+    "C-x r"   "rectangle/register/bookmark"
+    "C-x v"   "version control"
+    ;; prefixes for my personal bindings
+    "C-c a"   "applications"
+    "C-c a s" "shells"
+    "C-c b"   "borg"
+    "C-c c"   "compile-and-comments"
+    "C-c e"   "eval"
+    "C-c f"   "files"
+    "C-c F"   "frames"
+    "C-S-h"   "help(ful)"
+    "C-c m"   "multiple-cursors"
+    "C-c p"   "projectile"
+    "C-c p s" "projectile/search"
+    "C-c p x" "projectile/execute"
+    "C-c p 4" "projectile/other-window"
+    "C-c q"   "boxquote"
+    "s-g"     "magit"
+    "s-o"     "outline"
+    "s-t"     "themes")
+
+  ;; prefixes for major modes
+  (which-key-add-major-mode-key-based-replacements 'message-mode
+    "C-c f"   "footnote")
+  (which-key-add-major-mode-key-based-replacements 'org-mode
+    "C-c C-v" "org-babel")
+  (which-key-add-major-mode-key-based-replacements 'web-mode
+    "C-c C-a" "web/attributes"
+    "C-c C-b" "web/blocks"
+    "C-c C-d" "web/dom"
+    "C-c C-e" "web/element"
+    "C-c C-t" "web/tags")
+
+  (which-key-mode))
+#+end_src
+
+** theme
+
+#+begin_src emacs-lisp
+(add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
+(load-theme 'tangomod t)
+#+end_src
+
+** smart-mode-line
+
+#+begin_src emacs-lisp
+(use-package smart-mode-line
+  :demand
+  :config
+  (sml/setup))
+#+end_src
+
+** doom-themes
+
+#+begin_src emacs-lisp
+(use-package doom-themes)
+#+end_src
+
+** theme helper functions
+
+#+begin_src emacs-lisp
+(defun a/lights-on ()
+  "Enable my favourite light theme."
+  (interactive)
+  (mapc #'disable-theme custom-enabled-themes)
+  (load-theme 'tangomod t)
+  (sml/apply-theme 'automatic))
+
+(defun a/lights-off ()
+  "Go dark."
+  (interactive)
+  (mapc #'disable-theme custom-enabled-themes)
+  (load-theme 'doom-tomorrow-night t)
+  (sml/apply-theme 'automatic))
+
+(bind-keys
+ ("s-t d" . a/lights-off)
+ ("s-t l" . a/lights-on))
+#+end_src
+
+** [[https://github.com/bbatsov/crux][crux]]
+
+#+begin_src emacs-lisp
+(use-package crux
+  :defer 1
+  :bind (("C-c b k" . crux-kill-other-buffers)
+         ("C-c d"   . crux-duplicate-current-line-or-region)
+         ("C-c D"   . crux-duplicate-and-comment-current-line-or-region)
+         ("C-c f c" . crux-copy-file-preserve-attributes)
+         ("C-c f d" . crux-delete-file-and-buffer)
+         ("C-c f r" . crux-rename-file-and-buffer)
+         ("C-c j"   . crux-top-join-line)
+         ("C-S-j"   . crux-top-join-line)))
+#+end_src
+
+** [[https://github.com/alezost/mwim.el][mwim]]
+
+#+begin_src emacs-lisp
+(use-package mwim
+  :bind (("C-a"    . mwim-beginning-of-code-or-line)
+         ("C-e"    . mwim-end-of-code-or-line)
+         ("<home>" . mwim-beginning-of-line-or-code)
+         ("<end>"  . mwim-end-of-line-or-code)))
+#+end_src
+
+** projectile
+
+#+begin_src emacs-lisp
+(use-package projectile
+  :bind-keymap ("C-c p" . projectile-command-map)
+  :config
+  (projectile-mode)
+
+  (defun my-projectile-invalidate-cache (&rest _args)
+    ;; ignore the args to `magit-checkout'
+    (projectile-invalidate-cache nil))
+
+  (eval-after-load 'magit-branch
+    '(progn
+       (advice-add 'magit-checkout
+                   :after #'my-projectile-invalidate-cache)
+       (advice-add 'magit-branch-and-checkout
+                   :after #'my-projectile-invalidate-cache)))
+  :custom (projectile-completion-system 'ivy))
+#+end_src
+
+** [[https://github.com/Wilfred/helpful][helpful]]
+
+#+begin_src emacs-lisp
+(use-package helpful
+  :defer 1
+  :bind
+  (("C-S-h c" . helpful-command)
+   ("C-S-h f" . helpful-callable)        ; helpful-function
+   ("C-S-h v" . helpful-variable)
+   ("C-S-h k" . helpful-key)
+   ("C-S-h p" . helpful-at-point)))
+#+end_src
+
+** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
+
+Make =*scratch*= and =*Messages*= unkillable.
+
+#+begin_src emacs-lisp
+(use-package unkillable-scratch
+  :defer 3
+  :config
+  (unkillable-scratch 1)
+  :custom
+  (unkillable-scratch-behavior 'do-nothing)
+  (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
+#+end_src
+
+** [[https://github.com/davep/boxquote.el][boxquote.el]]
+
+#+begin_example
+,----
+| make pretty boxed quotes like this
+`----
+#+end_example
+
+#+begin_src emacs-lisp
+(use-package boxquote
+  :defer 3
+  :bind
+  (:prefix-map a/boxquote-prefix-map
+   :prefix "C-c q"
+   ("b"   . boxquote-buffer)
+   ("B"   . boxquote-insert-buffer)
+   ("d"   . boxquote-defun)
+   ("F"   . boxquote-insert-file)
+   ("hf"  . boxquote-describe-function)
+   ("hk"  . boxquote-describe-key)
+   ("hv"  . boxquote-describe-variable)
+   ("hw"  . boxquote-where-is)
+   ("k"   . boxquote-kill)
+   ("p"   . boxquote-paragraph)
+   ("q"   . boxquote-boxquote)
+   ("r"   . boxquote-region)
+   ("s"   . boxquote-shell-command)
+   ("t"   . boxquote-text)
+   ("T"   . boxquote-title)
+   ("u"   . boxquote-unbox)
+   ("U"   . boxquote-unbox-region)
+   ("y"   . boxquote-yank)
+   ("M-q" . boxquote-fill-paragraph)
+   ("M-w" . boxquote-kill-ring-save)))
+#+end_src
+
+Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
+
+** orgalist
+
+#+begin_src emacs-lisp
+(use-package orgalist
+  :after message
+  :hook (message-mode . orgalist-mode))
+#+end_src
+
+** typo.el
+
+#+begin_src emacs-lisp
+(use-package typo
+  :defer 2
+  :config
+  (typo-global-mode 1)
+  :hook (text-mode . typo-mode))
+#+end_src
+
+** hl-todo
+
+#+begin_src emacs-lisp
+(use-package hl-todo
+  :defer 4
+  :config
+  (global-hl-todo-mode))
+#+end_src
+
+** shrink-path
+
+#+begin_src emacs-lisp
+(use-package shrink-path
+  :defer 2
+  :after eshell
+  :config
+  (defun +eshell/prompt ()
+    (let ((base/dir (shrink-path-prompt default-directory)))
+      (concat (propertize (car base/dir)
+                          'face 'font-lock-comment-face)
+              (propertize (cdr base/dir)
+                          'face 'font-lock-constant-face)
+              (propertize (+eshell--current-git-branch)
+                          'face 'font-lock-function-name-face)
+              "\n"
+              (propertize "λ" 'face 'eshell-prompt-face)
+              ;; needed for the input text to not have prompt face
+              (propertize " " 'face 'default))))
+
+  (defun +eshell--current-git-branch ()
+    (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
+                             when (string-match "^\*" match)
+                             collect match))))
+      (if (not (eq branch nil))
+          (concat " " (substring branch 2))
+        "")))
+  (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
+                         eshell-prompt-function #'+eshell/prompt))
+#+end_src
+
+** [[https://github.com/peterwvj/eshell-up][eshell-up]]
+
+#+begin_src emacs-lisp
+(use-package eshell-up
+  :after eshell
+  :commands eshell-up)
+#+end_src
+
+** multi-term
+
+#+begin_src emacs-lisp
+(use-package multi-term
+  :defer 1
+  :bind (("C-c a s m" . multi-term-dedicated-toggle)
+         :map term-mode-map
+         ("C-c C-j" . term-char-mode)
+         :map term-raw-map
+         ("C-c C-j" . term-line-mode))
+  :config
+  (setq multi-term-program "/bin/screen"
+        ;; TODO: add separate bindings for connecting to existing
+        ;; session vs. always creating a new one
+        multi-term-dedicated-select-after-open-p t
+        multi-term-dedicated-window-height 20
+        multi-term-dedicated-max-window-height 30
+        term-bind-key-alist
+        '(("C-c C-c" . term-interrupt-subjob)
+          ("C-c C-e" . term-send-esc)
+          ("C-k" . kill-line)
+          ("C-y" . term-paste)
+          ("M-f" . term-send-forward-word)
+          ("M-b" . term-send-backward-word)
+          ("M-p" . term-send-up)
+          ("M-n" . term-send-down)
+          ("<C-backspace>" . term-send-backward-kill-word)
+          ("<M-DEL>" . term-send-backward-kill-word)
+          ("M-d" . term-send-delete-word)
+          ("M-," . term-send-raw)
+          ("M-." . comint-dynamic-complete))
+        term-unbind-key-alist
+        '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
+#+end_src
+
+** page-break-lines
+
+#+begin_src emacs-lisp
+(use-package page-break-lines
+  :config
+  (global-page-break-lines-mode))
+#+end_src
+
+** expand-region
+
+#+begin_src emacs-lisp
+(use-package expand-region
+  :bind ("C-=" . er/expand-region))
+#+end_src
+
+** multiple-cursors
+
+#+begin_src emacs-lisp
+(use-package multiple-cursors
+  :bind
+  (("C-S-<mouse-1>" . mc/add-cursor-on-click)
+   (:prefix-map a/mc-prefix-map
+               :prefix "C-c m"
+               ("c" . mc/edit-lines)
+               ("n" . mc/mark-next-like-this)
+               ("p" . mc/mark-previous-like-this)
+               ("a" . mc/mark-all-like-this))))
+#+end_src
+
+* Email
+:PROPERTIES:
+:CUSTOM_ID: email
+:END:
+
+#+begin_src emacs-lisp
+(defvar a/maildir (expand-file-name "~/mail/"))
+(with-eval-after-load 'recentf
+  (add-to-list 'recentf-exclude a/maildir))
+#+end_src
+
+** Gnus
+
+#+begin_src emacs-lisp
+(setq
+ a/gnus-init-file  (no-littering-expand-etc-file-name "gnus")
+ mail-user-agent   'gnus-user-agent
+ read-mail-command 'gnus)
+
+(use-package gnus
+  :bind (("s-m"   . gnus)
+         ("s-M"   . gnus-unplugged))
+  :init
+  (setq
+   gnus-select-method '(nnnil "")
+   gnus-secondary-select-methods
+   '((nnimap "amin"
+            (nnimap-stream plain)
+            (nnimap-address "127.0.0.1")
+            (nnimap-server-port 143)
+            (nnimap-authenticator plain)
+            (nnimap-user "amin@aminb.org"))
+     (nnimap "uwaterloo"
+             (nnimap-stream plain)
+             (nnimap-address "127.0.0.1")
+             (nnimap-server-port 143)
+             (nnimap-authenticator plain)
+             (nnimap-user "abandali@uwaterloo.ca")))
+   gnus-message-archive-group "nnimap+amin:Sent"
+   gnus-parameters
+   '(("gnu.*"
+      (gcc-self . t)))
+   gnus-large-newsgroup  50
+   gnus-home-directory   (no-littering-expand-var-file-name "gnus/")
+   gnus-directory        (concat gnus-home-directory "news/")
+   message-directory     (concat gnus-home-directory "mail/")
+   nndraft-directory     (concat gnus-home-directory "drafts/")
+   gnus-save-newsrc-file nil
+   gnus-read-newsrc-file nil
+   gnus-interactive-exit nil
+   gnus-gcc-mark-as-read t))
+
+(use-package gnus-art
+  :config
+  (setq
+   gnus-visible-headers
+   (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
+   gnus-sorted-header-list
+   '("^From:" "^Subject:" "^Summary:" "^Keywords:"
+     "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
+     "^Newsgroups:" "List-Id:" "^Organization:"
+     "^User-Agent:" "^Date:")
+  ;; local-lapsed article dates
+  ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
+  gnus-article-date-headers '(user-defined)
+  gnus-article-time-format
+  (lambda (time)
+    (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
+           (local (article-make-date-line date 'local))
+           (combined-lapsed (article-make-date-line date
+                                                    'combined-lapsed))
+           (lapsed (progn
+                     (string-match " (.+" combined-lapsed)
+                     (match-string 0 combined-lapsed))))
+      (concat local lapsed))))
+  (bind-keys
+   :map gnus-article-mode-map
+   ("r"   . gnus-article-reply-with-original)
+   ("R"   . gnus-article-wide-reply-with-original)
+   ("M-L" . org-store-link)))
+
+(use-package gnus-sum
+  :bind (:map gnus-summary-mode-map
+              :prefix-map a/gnus-summary-prefix-map
+              :prefix "v"
+              ("r" . gnus-summary-reply)
+              ("w" . gnus-summary-wide-reply)
+              ("v" . gnus-summary-show-raw-article))
+  :config
+  (bind-keys
+   :map gnus-summary-mode-map
+   ("r"   . gnus-summary-reply-with-original)
+   ("R"   . gnus-summary-wide-reply-with-original)
+   ("M-L" . org-store-link))
+  :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
+
+(use-package gnus-msg
+  :config
+  (setq gnus-posting-styles
+        '((".*"
+           (address "amin@aminb.org")
+           (body "\nBest,\namin\n")
+           (eval (setq a/message-cite-say-hi t)))
+          ("gnu.*"
+           (address "bandali@gnu.org"))
+          ((header "subject" "ThankCRM")
+           (to "webmasters-comment@gnu.org")
+           (body "\nAdded  to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
+           (eval (setq a/message-cite-say-hi nil)))
+          ("nnimap\\+uwaterloo:.*"
+           (address "abandali@uwaterloo.ca")
+           (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
+
+(use-package gnus-topic
+  :hook (gnus-group-mode . gnus-topic-mode))
+
+(use-package gnus-agent
+  :config
+  (setq gnus-agent-synchronize-flags 'ask)
+  :hook (gnus-group-mode . gnus-agent-mode))
+
+(use-package gnus-group
+  :config
+  (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
+
+(use-package mm-decode
+  :config
+  (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
+#+end_src
+
+** sendmail
+
+#+begin_src emacs-lisp
+(use-package sendmail
+  :config
+  (setq sendmail-program "/usr/bin/msmtp"
+        ;; message-sendmail-extra-arguments '("-v" "-d")
+        mail-specify-envelope-from t
+        mail-envelope-from 'header))
+#+end_src
+
+** message
+
+#+begin_src emacs-lisp
+(use-package message
+  :config
+  (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
+  (defconst message-cite-style-bandali
+    '((message-cite-function  'message-cite-original)
+      (message-citation-line-function  'message-insert-formatted-citation-line)
+      (message-cite-reply-position 'traditional)
+      (message-yank-prefix  "> ")
+      (message-yank-cited-prefix  ">")
+      (message-yank-empty-prefix  ">")
+      (message-citation-line-format
+       (if a/message-cite-say-hi
+           (concat "Hi %F,\n\n" a/message-cite-style-format)
+         a/message-cite-style-format)))
+    "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
+  (setq message-cite-style 'message-cite-style-bandali
+        message-kill-buffer-on-exit t
+        message-send-mail-function 'message-send-mail-with-sendmail
+        message-sendmail-envelope-from 'header
+        message-dont-reply-to-names
+        "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
+        message-user-fqdn "aminb.org")
+  :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
+         (message-mode . flyspell-mode)
+         (message-mode . (lambda ()
+                           ;; (setq fill-column 65
+                           ;;       message-fill-column 65)
+                           (make-local-variable 'company-idle-delay)
+                           (setq company-idle-delay 0.2))))
+  ;; :custom-face
+  ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
+  ;; (message-header-to      ((t (:foreground "#111" :weight normal))))
+  ;; (message-header-cc      ((t (:foreground "#333" :weight normal))))
+  )
+
+(with-eval-after-load 'mml-sec
+  (setq mml-secure-openpgp-encrypt-to-self t
+        mml-secure-openpgp-sign-with-sender t))
+#+end_src
+
+** footnote
+
+Convenient footnotes in =message-mode=.
+
+#+begin_src emacs-lisp
+(use-package footnote
+  :after message
+  :bind
+  (:map message-mode-map
+        :prefix-map a/footnote-prefix-map
+        :prefix "C-c f"
+        ("a" . footnote-add-footnote)
+        ("b" . footnote-back-to-message)
+        ("c" . footnote-cycle-style)
+        ("d" . footnote-delete-footnote)
+        ("g" . footnote-goto-footnote)
+        ("r" . footnote-renumber-footnotes)
+        ("s" . footnote-set-style))
+  :config
+  (setq footnote-start-tag ""
+        footnote-end-tag   ""
+        footnote-style     'unicode))
+#+end_src
+
+** ebdb
+
+#+begin_src emacs-lisp
+(use-package ebdb
+  :after gnus
+  :bind (:map gnus-group-mode-map ("e" . ebdb))
+  :config
+  (setq ebdb-sources (no-littering-expand-var-file-name "ebdb"))
+  (with-eval-after-load 'swiper
+    (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t)))
+
+(use-package ebdb-com
+  :after ebdb)
+
+;; (use-package ebdb-complete
+;;   :after ebdb
+;;   :config
+;;   (ebdb-complete-enable))
+
+(use-package company-ebdb
+  :after (:all company message)
+  :config
+  (defun company-ebdb--post-complete (_) nil)
+  :hook
+  (message-mode . (lambda ()
+                    (add-to-list (make-local-variable 'company-backends)
+                                 'company-ebdb))))
+
+(use-package ebdb-gnus
+  :demand
+  :after ebdb
+  :custom
+  (ebdb-gnus-window-configuration
+   '(article
+     (vertical 1.0
+               (summary 0.25 point)
+               (horizontal 1.0
+                           (article 1.0)
+                           (ebdb-gnus 0.3))))))
+
+(use-package ebdb-mua
+  :demand
+  :after ebdb
+  ;; :custom (ebdb-mua-pop-up nil)
+  )
+
+;; (use-package ebdb-message
+;;   :after ebdb)
+
+
+;; (use-package ebdb-vcard
+;;   :after ebdb)
+#+end_src
+
+** COMMENT message-x
+
+#+begin_src emacs-lisp
+(use-package message-x
+  :custom
+  (message-x-completion-alist
+   (quote
+    (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
+     ((if
+          (boundp
+           (quote message-newgroups-header-regexp))
+          message-newgroups-header-regexp message-newsgroups-header-regexp)
+      . message-expand-group)))))
+#+end_src
+
+** COMMENT gnus-harvest
+
+#+begin_src emacs-lisp
+(use-package gnus-harvest
+  :commands gnus-harvest-install
+  :demand t
+  :config
+  (if (featurep 'message-x)
+      (gnus-harvest-install 'message-x)
+    (gnus-harvest-install)))
+#+end_src
+
+* Blogging
+:PROPERTIES:
+:CUSTOM_ID: blogging
+:END:
+
+** [[https://ox-hugo.scripter.co][ox-hugo]]
+
+#+begin_src emacs-lisp
+(use-package ox-hugo
+  :after ox)
+
+(use-package ox-hugo-auto-export
+  :load-path "lib/ox-hugo")
+#+end_src
+
+* Post initialization
+:PROPERTIES:
+:CUSTOM_ID: post-initialization
+:END:
+
+Display how long it took to load the init file.
 
 #+begin_src emacs-lisp
 (message "Loading %s...done (%.3fs)" user-init-file
          (float-time (time-subtract (current-time)
 
 #+begin_src emacs-lisp
 (message "Loading %s...done (%.3fs)" user-init-file
          (float-time (time-subtract (current-time)
-                                    ab--before-user-init-time)))
+                                    a/before-user-init-time)))
 #+end_src
 
 * Footer
 #+end_src
 
 * Footer
@@ -657,3 +2361,9 @@ Display how long it took to load the init file.
 #+begin_src emacs-lisp :comments none
 ;;; init.el ends here
 #+end_src
 #+begin_src emacs-lisp :comments none
 ;;; init.el ends here
 #+end_src
+
+* COMMENT Local Variables                                           :ARCHIVE:
+# Local Variables:
+# eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
+# eval: (when (featurep 'typo (typo-mode -1)))
+# End: