[emacs][wip] fix Org configurations
[~bandali/configs] / init.org
index eea005d..4e3ee52 100644 (file)
--- a/init.org
+++ b/init.org
@@ -104,28 +104,12 @@ file.
 #+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
 
-** Naming conventions
-
-The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s, found [[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]].
-
-#+begin_src emacs-lisp :comments none
-;; Naming conventions:
-;;
-;;   amin-...   public variables or non-interactive functions
-;;   amin--...  private anything (non-interactive), not safe for direct use
-;;   amin/...   an interactive function; safe for M-x or keybinding
-;;   amin|...   a hook function
-;;   amin*...   an advising function
-;;   amin@...   a hydra command
-;;   ...!       a macro
-#+end_src
-
 * Initial setup
 :PROPERTIES:
 :CUSTOM_ID: initial-setup
@@ -138,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
-(defvar amin--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)"
-         (float-time (time-subtract amin--before-user-init-time
+         (float-time (time-subtract a/before-user-init-time
                                     before-init-time)))
 #+end_src
 
@@ -151,9 +135,9 @@ frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
 startup time as well.
 
 #+begin_src emacs-lisp
-(defvar amin--gc-cons-threshold gc-cons-threshold)
-(defvar amin--gc-cons-percentage gc-cons-percentage)
-(defvar amin--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
@@ -168,9 +152,9 @@ done initializing.
 (add-hook
  'after-init-hook
  (lambda ()
-   (setq gc-cons-threshold amin--gc-cons-threshold
-         gc-cons-percentage amin--gc-cons-percentage
-         file-name-handler-alist amin--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*=
@@ -200,9 +184,9 @@ but for now I've decided to keep them enabled. See documentation for
 
 *** No =package.el=
 
-I can do all my package management things with Borg, and don't need
-Emacs' built-in =package.el=. Emacs 27 lets us disable =package.el= in
-the =early-init-file= (see [[https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=24acb31c04b4048b85311d794e600ecd7ce60d3b][here]]).
+I can do all my package management things with =straight.el=, and
+don't need Emacs' built-in =package.el=.  Emacs 27 lets us disable
+=package.el= in the =early-init-file= (see [[https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=24acb31c04b4048b85311d794e600ecd7ce60d3b][here]]).
 
 #+begin_src emacs-lisp :tangle early-init.el
 (setq package-enable-at-startup nil)
@@ -213,12 +197,36 @@ right now), and even when released it'll be long before most distros
 ship in their repos, I'll still put the old workaround with the
 commented call to ~package-initialize~ here anyway.
 
-#+begin_src emacs-lisp
+#+begin_src emacs-lisp :tangle no
 (setq package-enable-at-startup nil)
 ;; (package-initialize)
 #+end_src
 
-*** Borg
+Update: the above is not necessary, since =straight.el= automatically
+does that (and more).  See =straight-package-neutering-mode=.
+
+*** =straight.el=
+
+#+begin_src emacs-lisp
+(setq straight-repository-branch "develop")
+
+(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))
+
+(setq straight-use-package-by-default t)
+#+end_src
+
+*** COMMENT Borg
 
 #+begin_quote
 Assimilate Emacs packages as Git submodules
@@ -260,7 +268,7 @@ packages (in our case especially the latter) in a neatly organized way
 and without compromising on performance.
 
 #+begin_src emacs-lisp
-(require 'use-package)
+(straight-use-package 'use-package)
 (if nil  ; set to t when need to debug init
     (setq use-package-verbose t
           use-package-expand-minimally nil
@@ -270,7 +278,7 @@ and without compromising on performance.
         use-package-expand-minimally t))
 #+end_src
 
-*** Epkg
+*** COMMENT Epkg
 
 #+begin_quote
 Browse the Emacsmirror package database
@@ -286,7 +294,12 @@ database, low-level functions for querying the database, and a
   :bind
   (("C-c b d" . epkg-describe-package)
    ("C-c b p" . epkg-list-packages)
-   ("C-c b u" . epkg-update)))
+   ("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=
@@ -318,6 +331,7 @@ it it's own file. While at it, treat themes as safe.
 
 #+begin_src emacs-lisp
 (use-package custom
+  :straight nil
   :no-require t
   :config
   (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
@@ -372,6 +386,7 @@ See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.htm
 
 #+begin_src emacs-lisp
 (use-package server
+  :straight nil
   :defer 1
   :config (or (server-running-p) (server-mode)))
 #+end_src
@@ -433,7 +448,7 @@ Let’s define a function to conveniently disable this for certain
 buffers and/or modes.
 
 #+begin_src emacs-lisp
-(defun amin--no-mouse-autoselect-window ()
+(defun a/no-mouse-autoselect-window ()
   (make-local-variable 'mouse-autoselect-window)
   (setq mouse-autoselect-window nil))
 #+end_src
@@ -447,48 +462,35 @@ buffers and/or modes.
 
 ** Useful utilities
 
-#+begin_src emacs-lisp
-(defun amin-enlist (exp)
-  "Return EXP wrapped in a list, or as-is if already a list."
-(if (listp exp) exp (list exp)))
-
-; from https://github.com/hlissner/doom-emacs/commit/589108fdb270f24a98ba6209f6955fe41530b3ef
-(defmacro after! (features &rest body)
-  "A smart wrapper around `with-eval-after-load'. Supresses warnings during
-compilation."
-  (declare (indent defun) (debug t))
-  (list (if (or (not (bound-and-true-p byte-compile-current-file))
-                (dolist (next (amin-enlist features))
-                  (if (symbolp next)
-                      (require next nil :no-error)
-                    (load next :no-message :no-error))))
-            #'progn
-          #'with-no-warnings)
-        (cond ((symbolp features)
-               `(eval-after-load ',features '(progn ,@body)))
-              ((and (consp features)
-                    (memq (car features) '(:or :any)))
-               `(progn
-                  ,@(cl-loop for next in (cdr features)
-                             collect `(after! ,next ,@body))))
-              ((and (consp features)
-                    (memq (car features) '(:and :all)))
-               (dolist (next (cdr features))
-                 (setq body `(after! ,next ,@body)))
-               body)
-              ((listp features)
-               `(after! (:all ,@features) ,@body)))))
-#+end_src
-
 Convenience macro for =setq='ing multiple variables to the same value:
 
 #+begin_src emacs-lisp
-(defmacro setq-every! (value &rest vars)
+(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
@@ -659,14 +661,13 @@ Based on https://stackoverflow.com/a/17788551, with changes to use
 =cl-letf= instead of the now obsolete =flet=.
 
 #+begin_src emacs-lisp
-(use-package compile
-  :config
-  (defun amin--compilation-finish-function (buffer outstr)
+(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 #'amin--compilation-finish-function)
+  (setq compilation-finish-functions #'a/compilation-finish-function)
 
   (require 'cl-macs)
 
@@ -693,6 +694,12 @@ for this.
 ;; (setq replace-char-fold t)
 #+end_src
 
+*** Cursor shape
+
+#+begin_src emacs-lisp
+(setq-default cursor-type 'bar)
+#+end_src
+
 ** Bindings
 
 #+begin_src emacs-lisp
@@ -743,7 +750,7 @@ customizing it.
             'auto-compile-inhibit-compile-detached-git-head))
 #+end_src
 
-*** [[https://orgmode.org/][Org mode]]
+*** [[https://orgmode.org/][Org]]
 
 #+begin_quote
 Org mode is for keeping notes, maintaining TODO lists, planning
@@ -753,15 +760,62 @@ system.
 
 In short, my favourite way of life.
 
+First, we have to resort to a [[https://github.com/raxod502/straight.el#installing-org-with-straightel][hack]] to be able to use the correct
+latest version of Org from upstream.
+
+#+begin_src emacs-lisp
+  (use-package git)
+
+  (defun org-git-version ()
+    "The Git version of org-mode.
+  Inserted by installing org-mode or when a release is made."
+    (require 'git)
+    (let ((git-repo (expand-file-name
+                     "straight/repos/org/" user-emacs-directory)))
+      (string-trim
+       (git-run "describe"
+                "--match=release\*"
+                "--abbrev=6"
+                "HEAD"))))
+
+  (defun org-release ()
+    "The release version of org-mode.
+  Inserted by installing org-mode or when a release is made."
+    (require 'git)
+    (let ((git-repo (expand-file-name
+                     "straight/repos/org/" user-emacs-directory)))
+      (string-trim
+       (string-remove-prefix
+        "release_"
+        (git-run "describe"
+                 "--match=release\*"
+                 "--abbrev=0"
+                 "HEAD")))))
+
+(provide 'org-version)
+#+end_src
+
+We will use the =org-plus-contrib= package to get the whole deal:
+
+#+begin_src emacs-lisp
+(straight-use-package 'org-plus-contrib)
+#+end_src
+
+And here's where my actual Org configurations begin:
+
 #+begin_src emacs-lisp
 (use-package org
-  :defer 1
+  :straight nil
+  :defer 0.5
   :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
@@ -783,6 +837,7 @@ In short, my favourite way of life.
   '(org-latex-and-related ((t (:foreground "#b294bb")))))
 
 (use-package ox-latex
+  :straight nil
   :after ox
   :config
   (setq org-latex-listings 'listings
@@ -800,6 +855,7 @@ In short, my favourite way of life.
                t))
 
 (use-package ox-beamer
+  :straight nil
   :after ox)
 
 (use-package orgalist
@@ -809,22 +865,22 @@ In short, my favourite way of life.
 
 **** asynchronous tangle
 
-=amin/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
+=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
-(after! org
-  (defvar amin-show-async-tangle-results nil
+(with-eval-after-load 'org
+  (defvar a/show-async-tangle-results nil
     "Keep *emacs* async buffers around for later inspection.")
 
-  (defvar amin-show-async-tangle-time nil
+  (defvar a/show-async-tangle-time nil
     "Show the time spent tangling the file.")
 
-  (defvar amin-async-tangle-post-compile "make ti"
+  (defvar a/async-tangle-post-compile "make ti"
     "If non-nil, pass to `compile' after successful tangle.")
 
-  (defun amin/async-babel-tangle ()
+  (defun a/async-babel-tangle ()
     "Tangle org file asynchronously."
     (interactive)
     (let* ((file-tangle-start-time (current-time))
@@ -836,24 +892,24 @@ file.
        `(lambda ()
           (require 'org)
           (org-babel-tangle-file ,file))
-       (unless amin-show-async-tangle-results
+       (unless a/show-async-tangle-results
          `(lambda (result)
             (if result
                 (progn
                   (message "Tangled %s%s"
                            ,file-nodir
-                           (if amin-show-async-tangle-time
+                           (if a/show-async-tangle-time
                                (format " (%.3fs)"
                                        (float-time (time-subtract (current-time)
                                                                   ',file-tangle-start-time)))
                              ""))
-                  (when amin-async-tangle-post-compile
-                    (compile amin-async-tangle-post-compile)))
+                  (when a/async-tangle-post-compile
+                    (compile a/async-tangle-post-compile)))
               (message "Tangling %s failed" ,file-nodir))))))))
 
 (add-to-list
  'safe-local-variable-values
- '(eval add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local))
+ '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
 #+end_src
 
 *** [[https://magit.vc/][Magit]]
@@ -884,7 +940,7 @@ Not just how I do git, but /the/ way to do git.
   :custom-face (magit-diff-file-heading ((t (:weight normal)))))
 #+end_src
 
-*** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
+*** COMMENT [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
 
 #+begin_quote
 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
@@ -940,42 +996,44 @@ There's no way I could top that, so I won't attempt to.
   (defalias 'locate #'counsel-locate))
 #+end_src
 
-*** eshell
+*** COMMENT 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 amin/eshell-quit-or-delete-char (arg)
+  (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 amin/eshell-clear ()
+  (defun a/eshell-clear ()
     (interactive)
     (let ((inhibit-read-only t))
       (erase-buffer))
     (eshell-send-input))
 
-  (defun amin|eshell-setup ()
+  (defun a/eshell-setup ()
     (make-local-variable 'company-idle-delay)
-    (defvar company-idle-delay nil)
+    (defvar company-idle-delay)
+    (setq company-idle-delay nil)
     (bind-keys :map eshell-mode-map
-               ("C-d"   . amin/eshell-quit-or-delete-char)
-               ("C-S-l" . amin/eshell-clear)
+               ("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 . amin|eshell-setup)
+  :hook (eshell-mode . a/eshell-setup)
   :custom
   (eshell-hist-ignoredups t)
   (eshell-input-filter 'eshell-input-filter-initial-space))
 #+end_src
 
-*** Ibuffer
+*** COMMENT Ibuffer
 
 #+begin_src emacs-lisp
 (use-package ibuffer
@@ -1046,7 +1104,7 @@ There's no way I could top that, so I won't attempt to.
   :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
 #+end_src
 
-*** Outline
+*** COMMENT Outline
 
 #+begin_src emacs-lisp
 (use-package outline
@@ -1058,7 +1116,7 @@ There's no way I could top that, so I won't attempt to.
    ("<s-tab>"  . outline-toggle-children)
    ("M-p"      . outline-previous-visible-heading)
    ("M-n"      . outline-next-visible-heading)
-   :prefix-map amin--outline-prefix-map
+   :prefix-map a/outline-prefix-map
    :prefix "s-o"
    ("TAB" . outline-toggle-children)
    ("a"   . outline-hide-body)
@@ -1068,7 +1126,7 @@ There's no way I could top that, so I won't attempt to.
    ("s"   . outline-show-subtree)))
 #+end_src
 
-* Borg's =layer/essentials=
+* COMMENT Borg's =layer/essentials=
 :PROPERTIES:
 :CUSTOM_ID: borg-essentials
 :END:
@@ -1085,9 +1143,45 @@ TODO: break this giant source block down into individual org sections.
   (global-diff-hl-mode)
   (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
 
+(use-package ls-lisp
+  :custom (ls-lisp-dirs-first t))
+
 (use-package dired
   :defer t
-  :config (setq dired-listing-switches "-alh"))
+  :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)
+    (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
+              ("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))
 
 (use-package eldoc
   :when (version< "25" emacs-version)
@@ -1153,7 +1247,7 @@ TODO: break this giant source block down into individual org sections.
 
 #+end_src
 
-* Editing
+* COMMENT Editing
 :PROPERTIES:
 :CUSTOM_ID: editing
 :END:
@@ -1177,7 +1271,7 @@ TODO: break this giant source block down into individual org sections.
   (global-company-mode t))
 #+end_src
 
-* Syntax and spell checking
+* COMMENT Syntax and spell checking
 :PROPERTIES:
 :CUSTOM_ID: syntax-spell-checking
 :END:
@@ -1223,7 +1317,8 @@ TODO: break this giant source block down into individual org sections.
   (advice-add #'ispell-parse-output :filter-args
               #'endless/replace-quote))
 #+end_src
-* Programming modes
+
+* COMMENT Programming modes
 :PROPERTIES:
 :CUSTOM_ID: programming-modes
 :END:
@@ -1478,7 +1573,7 @@ instead.
   :defer t
   :mode "\\.html\\'"
   :config
-  (setq-every! 2
+  (a/setq-every 2
     web-mode-code-indent-offset
     web-mode-css-indent-offset
     web-mode-markup-indent-offset))
@@ -1595,7 +1690,7 @@ treemacs
   :load-path "lib/guix/elisp")
 #+end_src
 
-* Emacs enhancements
+* COMMENT Emacs enhancements
 :PROPERTIES:
 :CUSTOM_ID: emacs-enhancements
 :END:
@@ -1609,7 +1704,51 @@ Emacs package that displays available keybindings in popup
 #+begin_src emacs-lisp
 (use-package which-key
   :defer 1
-  :config (which-key-mode))
+  :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
@@ -1636,14 +1775,14 @@ Emacs package that displays available keybindings in popup
 ** theme helper functions
 
 #+begin_src emacs-lisp
-(defun amin/lights-on ()
+(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 amin/lights-off ()
+(defun a/lights-off ()
   "Go dark."
   (interactive)
   (mapc #'disable-theme custom-enabled-themes)
@@ -1651,8 +1790,8 @@ Emacs package that displays available keybindings in popup
   (sml/apply-theme 'automatic))
 
 (bind-keys
- ("s-t d" . amin/lights-off)
- ("s-t l" . amin/lights-on))
+ ("s-t d" . a/lights-off)
+ ("s-t l" . a/lights-on))
 #+end_src
 
 ** [[https://github.com/bbatsov/crux][crux]]
@@ -1698,7 +1837,8 @@ Emacs package that displays available keybindings in popup
        (advice-add 'magit-checkout
                    :after #'my-projectile-invalidate-cache)
        (advice-add 'magit-branch-and-checkout
-                   :after #'my-projectile-invalidate-cache))))
+                   :after #'my-projectile-invalidate-cache)))
+  :custom (projectile-completion-system 'ivy))
 #+end_src
 
 ** [[https://github.com/Wilfred/helpful][helpful]]
@@ -1714,53 +1854,6 @@ Emacs package that displays available keybindings in popup
    ("C-S-h p" . helpful-at-point)))
 #+end_src
 
-** [[https://github.com/knu/shell-toggle.el][shell-toggle]]
-
-#+begin_src emacs-lisp
-(use-package shell-toggle
-  :after eshell
-  :bind ("C-c a s e" . amin/shell-toggle)
-  :config
-  (defun amin/shell-toggle (make-cd)
-    "Toggle between the shell buffer and whatever buffer you are editing.
-With a prefix argument MAKE-CD also insert a \"cd DIR\" command
-into the shell, where DIR is the directory of the current buffer.
-
-When called in the shell buffer returns you to the buffer you were editing
-before calling this the first time.
-
-Options: `shell-toggle-goto-eob'"
-    (interactive "P")
-    ;; Try to decide on one of three possibilities:
-    ;; If not in shell-buffer, switch to it.
-    ;; If in shell-buffer, return to state before going to the shell-buffer
-    (if (eq (current-buffer) shell-toggle-shell-buffer)
-        (shell-toggle-buffer-return-from-shell)
-      (progn
-        (shell-toggle-buffer-goto-shell make-cd)
-        (if shell-toggle-full-screen-window-only (delete-other-windows)))))
-
-  ;; override to split horizontally instead
-  (defun shell-toggle-buffer-switch-to-other-window ()
-    "Switch to other window.
-If the current window is the only window in the current frame,
-create a new window and switch to it.
-
-\(This is less intrusive to the current window configuration than
-`switch-buffer-other-window')"
-    (let ((this-window (selected-window)))
-      (other-window 1)
-      ;; If we did not switch window then we only have one window and need to
-      ;; create a new one.
-      (if (eq this-window (selected-window))
-             (progn
-               (split-window-horizontally)
-            (other-window 1)))))
-
-  :custom
-  (shell-toggle-launch-shell 'shell-toggle-eshell))
-#+end_src
-
 ** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
 
 Make =*scratch*= and =*Messages*= unkillable.
@@ -1787,7 +1880,7 @@ Make =*scratch*= and =*Messages*= unkillable.
 (use-package boxquote
   :defer 3
   :bind
-  (:prefix-map amin--boxquote-prefix-map
+  (:prefix-map a/boxquote-prefix-map
    :prefix "C-c q"
    ("b"   . boxquote-buffer)
    ("B"   . boxquote-insert-buffer)
@@ -1813,42 +1906,6 @@ Make =*scratch*= and =*Messages*= unkillable.
 
 Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
 
-** COMMENT [[https://github.com/DarthFennec/highlight-indent-guides][highlight-indent-guides]]
-
-#+begin_src emacs-lisp
-(use-package highlight-indent-guides
-  :defer 3
-  :hook ((prog-mode . highlight-indent-guides-mode)
-         ;; (org-mode  . highlight-indent-guides-mode)
-         )
-  :config
-  (setq highlight-indent-guides-character ?\|)
-  (setq highlight-indent-guides-auto-enabled nil)
-  (setq highlight-indent-guides-method 'character)
-  (setq highlight-indent-guides-responsive 'top)
-  (set-face-foreground 'highlight-indent-guides-character-face "gainsboro")
-  (set-face-foreground 'highlight-indent-guides-top-character-face "grey40")) ; grey13 is nice too
-#+end_src
-
-** pdf-tools
-
-#+begin_src emacs-lisp
-(use-package pdf-tools
-  :defer t
-  :magic ("%PDF" . pdf-view-mode)
-  :config
-  (setq pdf-view-resize-factor 1.05)
-  (pdf-tools-install)
-  :bind
-  (:map pdf-view-mode-map
-        ("C-s" . isearch-forward)
-        ("C-r" . isearch-backward)
-        ("j"   . pdf-view-next-line-or-next-page)
-        ("k"   . pdf-view-previous-line-or-previous-page)
-        ("h"   . image-backward-hscroll)
-        ("l"   . image-forward-hscroll)))
-#+end_src
-
 ** typo.el
 
 #+begin_src emacs-lisp
@@ -1899,71 +1956,6 @@ Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
         ""))))
 #+end_src
 
-** COMMENT slack
-
-Hopefully temporary.
-
-#+begin_src emacs-lisp
-(use-package slack
-  :commands (slack-start)
-  :init
-  (eval-when-compile                    ; silence the byte-compiler
-    (defvar url-http-data nil)
-    (defvar url-http-extra-headers nil)
-    (defvar url-http-method nil)
-    (defvar url-callback-function nil)
-    (defvar url-callback-arguments nil)
-    (defvar oauth--token-data nil))
-  (setq slack-buffer-emojify t
-        slack-prefer-current-team t)
-  :config
-  (slack-register-team
-   :name "uw-apv"
-   :default t
-   :client-id uw-apv-client-id
-   :client-secret uw-apv-client-secret
-   :token uw-apv-token
-   :subscribed-channels '(general)
-   :full-and-display-names t)
-  (slack-register-team
-   :name "watform"
-   :default nil
-   :client-id watform-client-id
-   :client-secret watform-client-secret
-   :token watform-token
-   :subscribed-channels '(general)
-   :full-and-display-names t)
-  (add-to-list 'swiper-font-lock-exclude 'slack-message-buffer-mode t)
-  (setq lui-time-stamp-format "[%Y-%m-%d %H:%M:%S]"
-        lui-time-stamp-only-when-changed-p t
-        lui-time-stamp-position 'right)
-  :bind
-  (("C-c s s" . slack-start)
-   ("C-c s u" . slack-select-unread-rooms)
-   ("C-c s b" . slack-select-rooms)
-   ("C-c s t" . slack-change-current-team)
-   ("C-c s c" . slack-ws-close)
-   :map slack-mode-map
-   ("M-p"     . slack-buffer-goto-prev-message)
-   ("M-n"     . slack-buffer-goto-next-message)
-   ("C-c e"   . slack-message-edit)
-   ("C-c k"   . slack-message-delete)
-   ("C-c C-k" . slack-channel-leave)
-   ("C-c r a" . slack-message-add-reaction)
-   ("C-c r r" . slack-message-remove-reaction)
-   ("C-c r s" . slack-message-show-reaction-users)
-   ("C-c p l" . slack-room-pins-list)
-   ("C-c p a" . slack-message-pins-add)
-   ("C-c p r" . slack-message-pins-remove)
-   ("@"       . slack-message-embed-mention)
-   ("#"       . slack-message-embed-channel)))
-
-(use-package alert
-  :commands (alert)
-  :init
-  (setq alert-default-style 'notifier))
-#+end_src
-
 ** [[https://github.com/peterwvj/eshell-up][eshell-up]]
 
 #+begin_src emacs-lisp
@@ -1976,9 +1968,11 @@ Hopefully temporary.
 #+begin_src emacs-lisp
 (use-package multi-term
   :defer 1
-  :bind (("C-c C-j"     . term-line-mode)
-         ("C-c a s m m" . multi-term)
-         ("C-c a s m p" . multi-term-dedicated-toggle))
+  :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
@@ -2019,29 +2013,41 @@ Hopefully temporary.
   :bind ("C-=" . er/expand-region))
 #+end_src
 
-* Email
+** 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
+
+* COMMENT Email
 :PROPERTIES:
 :CUSTOM_ID: email
 :END:
 
 #+begin_src emacs-lisp
-(defvar amin-maildir (expand-file-name "~/mail/"))
-(after! recentf
-  (add-to-list 'recentf-exclude amin-maildir))
+(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
- amin-gnus-init-file (no-littering-expand-etc-file-name "gnus")
- mail-user-agent     'gnus-user-agent
- read-mail-command   'gnus)
+ 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 (("C-c m" . gnus)
-         ("C-c M" . gnus-unplugged)
-         ("s-m"   . gnus)
+  :bind (("s-m"   . gnus)
          ("s-M"   . gnus-unplugged))
   :init
   (setq
@@ -2104,7 +2110,7 @@ Hopefully temporary.
 
 (use-package gnus-sum
   :bind (:map gnus-summary-mode-map
-              :prefix-map amin--gnus-summary-prefix-map
+              :prefix-map a/gnus-summary-prefix-map
               :prefix "v"
               ("r" . gnus-summary-reply)
               ("w" . gnus-summary-wide-reply)
@@ -2115,7 +2121,7 @@ Hopefully temporary.
    ("r"   . gnus-summary-reply-with-original)
    ("R"   . gnus-summary-wide-reply-with-original)
    ("M-L" . org-store-link))
-  :hook (gnus-summary-mode . amin--no-mouse-autoselect-window))
+  :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
 
 (use-package gnus-msg
   :config
@@ -2123,13 +2129,13 @@ Hopefully temporary.
         '((".*"
            (address "amin@aminb.org")
            (body "\nBest,\namin\n")
-           (eval (setq amin--message-cite-say-hi t)))
+           (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 amin--message-cite-say-hi nil)))
+           (eval (setq a/message-cite-say-hi nil)))
           ("nnimap\\+uwaterloo:.*"
            (address "abandali@uwaterloo.ca")
            (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
@@ -2167,7 +2173,7 @@ Hopefully temporary.
 #+begin_src emacs-lisp
 (use-package message
   :config
-  (defconst amin--message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
+  (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)
@@ -2176,9 +2182,9 @@ Hopefully temporary.
       (message-yank-cited-prefix  ">")
       (message-yank-empty-prefix  ">")
       (message-citation-line-format
-       (if amin--message-cite-say-hi
-           (concat "Hi %F,\n\n" amin--message-cite-style-format)
-         amin--message-cite-style-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
@@ -2200,7 +2206,7 @@ Hopefully temporary.
   ;; (message-header-cc      ((t (:foreground "#333" :weight normal))))
   )
 
-(after! mml-sec
+(with-eval-after-load 'mml-sec
   (setq mml-secure-openpgp-encrypt-to-self t
         mml-secure-openpgp-sign-with-sender t))
 #+end_src
@@ -2214,7 +2220,7 @@ Convenient footnotes in =message-mode=.
   :after message
   :bind
   (:map message-mode-map
-        :prefix-map amin--footnote-prefix-map
+        :prefix-map a/footnote-prefix-map
         :prefix "C-c f"
         ("a" . footnote-add-footnote)
         ("b" . footnote-back-to-message)
@@ -2237,7 +2243,7 @@ Convenient footnotes in =message-mode=.
   :bind (:map gnus-group-mode-map ("e" . ebdb))
   :config
   (setq ebdb-sources (no-littering-expand-var-file-name "ebdb"))
-  (after! swiper
+  (with-eval-after-load 'swiper
     (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t)))
 
 (use-package ebdb-com
@@ -2281,39 +2287,6 @@ Convenient footnotes in =message-mode=.
 ;;   :after ebdb)
 #+end_src
 
-** COMMENT bbdb
-
-Manually install bbdb (=lisp/bbdb= copied from an ELPA-based setup),
-because installing it from source on Emacs 27 using the following
-submodule configuration for some reason doesn’t work and results in
-very strange errors when using any of the functions.
-
-#+begin_src conf :tangle no
-[submodule "bbdb"]
-       path = lib/bbdb
-       url = https://git.savannah.nongnu.org/git/bbdb.git
-       load-path = lisp
-       info-path = doc
-       build-step = ./autogen.sh
-       build-step = ./configure
-       build-step = make
-       build-step = make install
-#+end_src
-
-I tried using =borg-elpa= instead of doing it like this, but it added
-2 seconds to my startup time, which is unacceptable to me.
-
-#+begin_src emacs-lisp
-(use-package bbdb
-  :load-path "lisp/bbdb"
-  :init
-  (load (expand-file-name "lisp/bbdb/bbdb-autoloads.el" user-emacs-directory))
-  ;; (bbdb-mua-auto-update-init 'message)
-  (setq bbdb-mua-auto-update-p 'query
-        bbdb-complete-mail nil)
-  (bbdb-initialize 'gnus 'message))
-#+end_src
-
 ** COMMENT message-x
 
 #+begin_src emacs-lisp
@@ -2341,7 +2314,7 @@ I tried using =borg-elpa= instead of doing it like this, but it added
     (gnus-harvest-install)))
 #+end_src
 
-* Blogging
+* COMMENT Blogging
 :PROPERTIES:
 :CUSTOM_ID: blogging
 :END:
@@ -2366,7 +2339,7 @@ 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)
-                                    amin--before-user-init-time)))
+                                    a/before-user-init-time)))
 #+end_src
 
 * Footer
@@ -2380,6 +2353,6 @@ Display how long it took to load the init file.
 
 * COMMENT Local Variables                                           :ARCHIVE:
 # Local Variables:
-# eval: (add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local)
+# eval: ;; (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
 # eval: (typo-mode -1)
 # End: