about summary refs log tree commit diff
path: root/users
diff options
context:
space:
mode:
authorAspen Smith <grfn@gws.fyi>2024-02-20T21·31-0500
committeraspen <root@gws.fyi>2024-02-26T21·16+0000
commitacc2433b659b10d92cff78baac1207e469d249d3 (patch)
tree783bbb1e4863cc4f3addd2fe4ff9edf31eaf1b2d /users
parent7ecf43a1bd216d13ca061005e3f709bf5f1f49d9 (diff)
feat(aspen/emacs): Begin a complete emacs config revamp r/7614
I'm still on doom, but without all the cruft I've accumulated over the
last ~6 years it's actually good and fast and nice. Also, the config is
literate now!

The old emacs is still in .emacs.d, since occasionally I want to
reference it, but eventually I'll just get rid of it

Change-Id: Icda840d798594f7e6b188dba044494597d5f6043
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10999
Reviewed-by: aspen <root@gws.fyi>
Tested-by: BuildkiteCI
Diffstat (limited to 'users')
-rw-r--r--users/aspen/emacs/.gitignore2
-rw-r--r--users/aspen/emacs/config.org1375
-rw-r--r--users/aspen/emacs/init.el197
-rw-r--r--users/aspen/emacs/org-config.el141
-rw-r--r--users/aspen/emacs/packages.el12
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/annotation5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/benchmark-module26
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/header5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hedgehog-generator8
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hedgehog-property9
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hlint8
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/import-i4
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/inl6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/inline5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/language pragma6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/lens.field7
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/module32
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/shut up, hlint6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/test-group9
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/test-module27
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/undefined6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/action-type4
-rw-r--r--users/aspen/emacs/snippets/js2-mode/before7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/context7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/describe6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/expect5
-rw-r--r--users/aspen/emacs/snippets/js2-mode/function6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/header6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/it7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/it-pending5
-rw-r--r--users/aspen/emacs/snippets/js2-mode/module12
-rw-r--r--users/aspen/emacs/snippets/js2-mode/record7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/test7
-rw-r--r--users/aspen/emacs/snippets/nix-mode/fetchFromGitHub12
-rw-r--r--users/aspen/emacs/snippets/nix-mode/pythonPackage16
-rw-r--r--users/aspen/emacs/snippets/nix-mode/sha2567
-rw-r--r--users/aspen/emacs/snippets/org-mode/SQL source block6
-rw-r--r--users/aspen/emacs/snippets/org-mode/combat13
-rw-r--r--users/aspen/emacs/snippets/org-mode/date5
-rw-r--r--users/aspen/emacs/snippets/org-mode/date-time5
-rw-r--r--users/aspen/emacs/snippets/org-mode/description7
-rw-r--r--users/aspen/emacs/snippets/org-mode/nologdone5
-rw-r--r--users/aspen/emacs/snippets/org-mode/python source block6
-rw-r--r--users/aspen/emacs/snippets/org-mode/reveal6
-rw-r--r--users/aspen/emacs/snippets/org-mode/transaction7
-rw-r--r--users/aspen/emacs/snippets/prolog-mode/tests11
-rw-r--r--users/aspen/emacs/snippets/prolog-mode/use-module7
-rw-r--r--users/aspen/emacs/snippets/python-mode/add_column5
-rw-r--r--users/aspen/emacs/snippets/python-mode/decorate15
-rw-r--r--users/aspen/emacs/snippets/python-mode/dunder7
-rw-r--r--users/aspen/emacs/snippets/python-mode/name7
-rw-r--r--users/aspen/emacs/snippets/python-mode/op.get_bind.execute7
-rw-r--r--users/aspen/emacs/snippets/python-mode/pdb7
-rw-r--r--users/aspen/emacs/snippets/rust-mode/#[macro_use]5
-rw-r--r--users/aspen/emacs/snippets/rust-mode/async test10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/benchmark10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/proptest10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/test-module11
-rw-r--r--users/aspen/emacs/snippets/rust-mode/tests9
-rw-r--r--users/aspen/emacs/snippets/snippet-mode/indent5
-rw-r--r--users/aspen/emacs/snippets/sql-mode/count(*) group by5
-rw-r--r--users/aspen/emacs/snippets/terraform-mode/variable11
-rw-r--r--users/aspen/emacs/snippets/text-mode/date5
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/expect-test9
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/module9
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/test-module10
66 files changed, 2245 insertions, 0 deletions
diff --git a/users/aspen/emacs/.gitignore b/users/aspen/emacs/.gitignore
new file mode 100644
index 0000000000..f5236c1235
--- /dev/null
+++ b/users/aspen/emacs/.gitignore
@@ -0,0 +1,2 @@
+custom.el
+config.el
diff --git a/users/aspen/emacs/config.org b/users/aspen/emacs/config.org
new file mode 100644
index 0000000000..f367e49a65
--- /dev/null
+++ b/users/aspen/emacs/config.org
@@ -0,0 +1,1375 @@
+# Local variables:
+# eval: (paxedit-mode 1)
+# eval: (display-line-numbers-mode 1)
+# eval: (flyspell-mode -1)
+# eval: (org-config-mode 1)
+# End:
+
+#+title: Emacs Config
+
+* Utils
+#+begin_src elisp :tangle yes
+(use-package! dash)
+#+end_src
+
+** Elisp extras
+
+#+begin_src elisp :tangle yes
+(defmacro comment (&rest _body)
+  "Comment out one or more s-expressions"
+  nil)
+
+(defun inc (x) "Returns x + 1" (+ 1 x))
+(defun dec (x) "Returns x - 1" (- x 1))
+
+(defun average (ns)
+  "Arithmetic mean of xs"
+  (if (null ns) nil
+    (/ (apply #'+ ns)
+       (length ns))))
+
+(defun alist-set (alist-symbol key value)
+  "Set VALUE of a KEY in ALIST-SYMBOL."
+  (set alist-symbol (cons (list key value) (assq-delete-all key (eval alist-symbol)))))
+
+(defun rx-words (&rest words)
+  (rx-to-string
+   `(and symbol-start (group (or ,@words)) symbol-end)))
+#+end_src
+
+#+RESULTS:
+: rx-words
+
+#+begin_src elisp :tangle no :results example
+(average (list 1 2 3 4))
+#+end_src
+
+#+RESULTS:
+: 2
+
+** Text editing utils
+*** Reading strings
+#+begin_src elisp :tangle yes
+(defun get-char (&optional point)
+  "Get the character at the given `point' (defaulting to the current point),
+without properties"
+  (let ((point (or point (point))))
+    (buffer-substring-no-properties point (+ 1 point))))
+
+(defun get-line (&optional lineno)
+  "Read the line number `lineno', or the current line if `lineno' is nil, and
+return it as a string stripped of all text properties"
+  (let ((current-line (line-number-at-pos)))
+    (if (or (not lineno)
+            (= current-line lineno))
+        (thing-at-point 'line t)
+      (save-mark-and-excursion
+       (line-move (- lineno (line-number-at-pos)))
+       (thing-at-point 'line t)))))
+
+(defun get-line-point ()
+  "Get the position in the current line of the point"
+  (- (point) (line-beginning-position)))
+
+;; Moving in the file
+
+(defun goto-line-char (pt)
+  "Moves the point to the given position expressed as an offset from the start
+of the line"
+  (goto-char (+ (line-beginning-position) pt)))
+
+(defun goto-eol ()
+  "Moves to the end of the current line"
+  (goto-char (line-end-position)))
+
+(defun goto-regex-on-line (regex)
+  "Moves the point to the first occurrence of `regex' on the current line.
+Returns nil if the regex did not match, non-nil otherwise"
+  (when-let ((current-line (get-line))
+             (line-char (string-match regex current-line)))
+    (goto-line-char line-char)))
+
+(defun goto-regex-on-line-r (regex)
+  "Moves the point to the *last* occurrence of `regex' on the current line.
+Returns nil if the regex did not match, non-nil otherwise"
+  (when-let ((current-line (get-line))
+             (modified-regex (concat ".*\\(" regex "\\)"))
+             (_ (string-match modified-regex current-line))
+             (match-start (match-beginning 1)))
+    (goto-line-char match-start)))
+#+end_src
+
+#+begin_src elisp :tangle no
+(progn
+  (string-match (rx (and (zero-or-more anything)
+                         (group "foo" "foo")))
+                "foofoofoo")
+  (match-beginning 1))
+#+end_src
+
+#+RESULTS:
+: 3
+*** Changing file contents
+#+begin_src elisp :tangle yes
+(defmacro saving-excursion (&rest body)
+  `(λ! () (save-excursion ,@body)))
+
+(defun delete-line ()
+  "Remove the line at the current point"
+  (delete-region (line-beginning-position)
+                 (inc (line-end-position))))
+
+(defmacro modify-then-indent (&rest body)
+  "Modify text in the buffer according to body, then re-indent from where the
+  cursor started to where the cursor ended up, then return the cursor to where
+  it started."
+  `(let ((beg (line-beginning-position))
+         (orig-line-char (- (point) (line-beginning-position))))
+     (atomic-change-group
+       (save-mark-and-excursion
+        ,@body
+        (evil-indent beg (+ (line-end-position) 1))))
+     (goto-line-char orig-line-char)))
+
+(pcase-defmacro s-starts-with (prefix)
+  `(pred (s-starts-with-p ,prefix)))
+
+(pcase-defmacro s-contains (needle &optional ignore-case)
+  `(pred (s-contains-p ,needle
+                       ,@(when ignore-case (list ignore-case)))))
+#+end_src
+
+#+RESULTS:
+: s-contains--pcase-macroexpander
+
+#+begin_src elisp :tangle no
+(pcase "foo"
+  ((s-contains "bar") 1)
+  ((s-contains "o") 2))
+#+end_src
+
+#+RESULTS:
+: 2
+
+** Evil utils
+#+begin_src elisp :tangle yes
+(defmacro define-move-and-insert
+    (name &rest body)
+  `(defun ,name (count &optional vcount skip-empty-lines)
+     ;; Following interactive form taken from the source for `evil-insert'
+     (interactive
+      (list (prefix-numeric-value current-prefix-arg)
+            (and (evil-visual-state-p)
+                 (memq (evil-visual-type) '(line block))
+                 (save-excursion
+                   (let ((m (mark)))
+                     ;; go to upper-left corner temporarily so
+                     ;; `count-lines' yields accurate results
+                     (evil-visual-rotate 'upper-left)
+                     (prog1 (count-lines evil-visual-beginning evil-visual-end)
+                       (set-mark m)))))
+            (evil-visual-state-p)))
+     (atomic-change-group
+       ,@body
+       (evil-insert count vcount skip-empty-lines))))
+#+end_src
+
+#+RESULTS:
+: define-move-and-insert
+
+* Name and email
+#+begin_src emacs-lisp
+(setq user-full-name "Aspen Smith"
+      user-mail-address "root@gws.fyi")
+#+end_src
+
+#+RESULTS:
+: root@gws.fyi
+
+* Visual style
+#+begin_src elisp :tangle yes
+(let ((font-family (pcase system-type
+                     ('darwin "MesloLGSDZ NF")
+                     ('gnu/linux "Meslo LGSDZ Nerd Font"))))
+  (setq doom-font (font-spec :family font-family :size 14)
+        doom-big-font (font-spec :family font-family :size 24)
+        doom-big-font-increment 5
+        doom-variable-pitch-font (font-spec :family font-family)
+        doom-theme 'doom-solarized-light))
+
+(setq display-line-numbers-type t)
+
+(setq doom-modeline-buffer-file-name-style 'relative-to-project
+      doom-modeline-modal-icon nil
+      doom-modeline-github t
+      doom-modeline-height 12)
+#+end_src
+
+#+RESULTS:
+: 12
+
+#+begin_src elisp :tangle yes
+(setq whitespace-style '(face lines-tail))
+(global-whitespace-mode t)
+(add-hook 'org-mode-hook (lambda () (whitespace-mode -1)) t)
+#+end_src
+
+#+RESULTS:
+| er/add-org-mode-expansions | +aspen/org-setup | +lookup--init-org-mode-handlers-h | (closure (t) (&rest _) (add-hook 'before-save-hook 'org-encrypt-entries nil t)) | #[0 \300\301\302\303\304$\207 [add-hook change-major-mode-hook org-fold-show-all append local] 5] | #[0 \301\211\207 [imenu-create-index-function org-imenu-get-tree] 2] | doom-disable-show-paren-mode-h | doom-disable-show-trailing-whitespace-h | +org-make-last-point-visible-h | org-appear-mode | org-fancy-priorities-mode | org-superstar-mode | evil-org-mode | toc-org-enable | #[0 \300\301\302\303\304$\207 [add-hook change-major-mode-hook org-babel-show-result-all append local] 5] | org-babel-result-hide-spec | org-babel-hide-all-hashes | flyspell-mode | embrace-org-mode-hook | org-eldoc-load | +literate-enable-recompile-h | (lambda nil (whitespace-mode -1)) |
+
+** Theme
+[[https://davidjohnstone.net/lch-lab-colour-gradient-picker][LAB colour gradient picker]] is a good tool for trying to find "halfway points" between two colours
+
+*** Variables
+#+begin_src elisp :tangle no
+(rainbow-mode)
+#+end_src
+
+#+RESULTS:
+: t
+
+#+name: solarized-vars
+#+begin_src elisp :tangle yes
+(setq +solarized-s-base03    "#002b36"
+      +solarized-s-base02    "#073642"
+      ;; emphasized content
+      +solarized-s-base01    "#586e75"
+      ;; primary content
+      +solarized-s-base00    "#657b83"
+      +solarized-s-base0     "#839496"
+      ;; comments
+      +solarized-s-base1     "#93a1a1"
+      ;; background highlight light
+      +solarized-s-base2     "#eee8d5"
+      ;; background light
+      +solarized-s-base3     "#fdf6e3"
+
+      +solarized-halfway-highlight "#f5efdc"
+
+      ;; Solarized accented colors
+      +solarized-yellow    "#b58900"
+      +solarized-orange    "#cb4b16"
+      +solarized-red       "#dc322f"
+      +solarized-magenta   "#d33682"
+      +solarized-violet    "#6c71c4"
+      +solarized-blue      "#268bd2"
+      +solarized-cyan      "#2aa198"
+      +solarized-green     "#859900"
+
+      ;; Darker and lighter accented colors
+      ;; Only use these in exceptional circumstances!
+      +solarized-yellow-d  "#7B6000"
+      +solarized-yellow-l  "#DEB542"
+      +solarized-orange-d  "#8B2C02"
+      +solarized-orange-l  "#F2804F"
+      +solarized-red-d     "#990A1B"
+      +solarized-red-l     "#FF6E64"
+      +solarized-magenta-d "#93115C"
+      +solarized-magenta-l "#F771AC"
+      +solarized-violet-d  "#3F4D91"
+      +solarized-violet-l  "#9EA0E5"
+      +solarized-blue-d    "#00629D"
+      +solarized-blue-l    "#69B7F0"
+      +solarized-cyan-d    "#00736F"
+      +solarized-cyan-l    "#69CABF"
+      +solarized-green-d   "#546E00"
+      +solarized-green-l   "#B4C342")
+#+end_src
+
+#+RESULTS: solarized-vars
+: #B4C342
+
+*** Overrides
+
+#+name: overrides-for-solarized-light
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(cursor :background ,+solarized-s-base00)
+  `(font-lock-doc-face :foreground ,+solarized-s-base1)
+  `(font-lock-preprocessor-face :foreground ,+solarized-red :bold nil)
+  `(font-lock-keyword-face :foreground ,+solarized-green :bold nil)
+  `(font-lock-builtin-face :foreground ,+solarized-s-base01 :bold t)
+  `(font-lock-function-name-face :foreground ,+solarized-blue)
+  `(font-lock-constant-face :foreground ,+solarized-blue)
+  `(font-lock-type-face :italic nil)
+  `(highlight-numbers-number :bold nil)
+  `(highlight :background ,+solarized-s-base2)
+  `(solaire-hl-line-face :background ,+solarized-halfway-highlight)
+  `(hl-line :background ,+solarized-s-base2)
+
+  `(linum :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(line-number :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(line-number-current-line :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(fringe :background ,+solarized-s-base2)
+
+  `(whitespace-line :foreground ,+solarized-red :underline t)
+
+  `(haskell-operator-face :foreground ,+solarized-green)
+  `(haskell-keyword-face :foreground ,+solarized-cyan)
+
+  `(magit-branch-local :foreground ,+solarized-blue :bold t)
+  `(magit-branch-remote :foreground ,+solarized-green :bold t)
+  `(magit-branch-remote-head :foreground ,+solarized-green :bold t :box t)
+  `(magit-branch-current :box t :bold t)
+  `(magit-header-line :background nil :foreground ,+solarized-yellow :bold t :box nil)
+  `(diff-refine-added :foreground "#dbdb9c" :background "#5b6e35" :bold nil)
+  `(magit-diff-added-highlight :foreground "#657827" :background "#efeac7" :bold nil)
+  `(diff-refine-removed :background "#8e433d" :foreground "#ffb9a1" :bold nil)
+  `(magit-diff-removed-highlight :foreground "#a33c35" :background "#ffdec8" :bold nil)
+  `(magit-diff-hunk-heading :background "#f8e8c6" :foreground "#876d26" :bold nil)
+  `(magit-diff-hunk-heading-highlight :background "#f1d49b" :foreground "#766634" :bold nil)
+  `(magit-section-heading :foreground "#b58900")
+  `(magit-filename :foreground ,+solarized-s-base00)
+  `(magit-diff-context-highlight :background ,+solarized-halfway-highlight)
+
+  `(transient-delimiter :foreground ,+solarized-s-base1)
+  `(transient-inapt-suffix :foreground ,+solarized-s-base1)
+  `(transient-inactive-value :foreground ,+solarized-s-base1)
+  `(transient-inactive-argument :foreground ,+solarized-s-base1)
+  `(transient-key-exit :foreground ,+solarized-green :bold t)
+  `(transient-key-stay :foreground ,+solarized-blue :bold t)
+  )
+  #+end_src
+
+#+RESULTS: overrides-for-solarized-light
+| doom--customize-themes-h-91 | doom--customize-themes-h-92 | doom--customize-themes-h-93 | doom--customize-themes-h-94 | doom--customize-themes-h-95 | doom--customize-themes-h-96 | doom--customize-themes-h-97 | doom--customize-themes-h-98 |
+
+* Keybindings and navigation
+Get the hell out of here, snipe!
+#+begin_src elisp :tangle yes
+(remove-hook 'doom-first-input-hook #'evil-snipe-mode)
+#+end_src
+
+** Flycheck
+#+begin_src elisp :tangle yes
+(evil-set-command-property 'flycheck-next-error :repeat nil)
+(evil-set-command-property 'flycheck-prev-error :repeat nil)
+(evil-set-command-property 'flycheck-previous-error :repeat nil)
+
+(map!
+ (:map flycheck-mode-map
+  :m  "]e" #'flycheck-next-error
+  :m  "[e" #'flycheck-previous-error))
+#+end_src
+
+#+RESULTS:
+
+** Smerge
+#+begin_src elisp :tangle yes
+(evil-set-command-property 'smerge-next :repeat nil)
+(evil-set-command-property 'smerge-prev :repeat nil)
+
+(map!
+ :n "] n" #'smerge-next
+ :n "[ n" #'smerge-prev
+ (:leader
+  (:desc "smerge" :prefix "g m"
+   :desc "Keep Current" :n "SPC" #'smerge-keep-current
+   :desc "Keep All"     :n "a" #'smerge-keep-all
+   :desc "Keep Upper"   :n "u" #'smerge-keep-upper
+   :desc "Keep Lower"   :n "l" #'smerge-keep-lower)))
+t
+ #+end_src
+
+#+RESULTS:
+: t
+
+** Vinegar-style dired
+#+begin_src elisp :tangle yes
+(defun dired-mode-p () (eq 'dired-mode major-mode))
+
+(defun aspen/dired-minus ()
+  (interactive)
+  (if (dired-mode-p)
+      (dired-up-directory)
+    (when buffer-file-name
+      (-> (buffer-file-name)
+          (f-dirname)
+          (dired)))))
+
+(map!
+ :n "-" #'aspen/dired-minus
+ (:map dired-mode-map
+       "-" #'aspen/dired-minus))
+#+end_src
+
+#+RESULTS:
+
+** Lisp mappings
+*** Use paxedit
+#+begin_src elisp :tangle yes
+(use-package! paxedit
+  :hook ((emacs-lisp-mode . paxedit-mode)
+         (clojure-mode . paxedit-mode)
+         (common-lisp-mode . paxedit-mode)))
+#+end_src
+
+#+RESULTS:
+| paxedit-mode |
+
+*** Paxedit functions
+
+#+begin_src elisp :tangle yes
+(define-move-and-insert aspen/insert-at-sexp-end
+  (when (not (equal (get-char) "("))
+    (backward-up-list))
+  (forward-sexp)
+  (backward-char))
+
+(define-move-and-insert aspen/insert-at-sexp-start
+  (backward-up-list)
+  (forward-char))
+
+(define-move-and-insert aspen/insert-at-form-start
+  (backward-sexp)
+  (backward-char)
+  (insert " "))
+
+(define-move-and-insert aspen/insert-at-form-end
+  (forward-sexp)
+  (insert " "))
+
+(defun aspen/paxedit-kill (&optional n)
+  (interactive "p")
+  (or (paxedit-comment-kill)
+      (when (paxedit-symbol-cursor-within?)
+        (paxedit-symbol-kill))
+      (paxedit-implicit-sexp-kill n)
+      (paxedit-sexp-kill n)
+      (message paxedit-message-kill)))
+#+end_src
+
+#+RESULTS:
+: aspen/paxedit-kill
+
+*** Paxedit mappings
+#+begin_src elisp :tangle yes
+(map!
+ (:after paxedit
+         (:map paxedit-mode-map
+          :i ";"                          #'paxedit-insert-semicolon
+          :i "("                          #'paxedit-open-round
+          :i "["                          #'paxedit-open-bracket
+          :i "{"                          #'paxedit-open-curly
+          :n [remap evil-yank-line]       #'paxedit-copy
+          :n [remap evil-delete-line]     #'aspen/paxedit-kill
+          :n "g o"                        #'paxedit-sexp-raise
+          :n [remap evil-join-whitespace] #'paxedit-compress
+          :n "g S"                        #'paxedit-format-1
+          :n "g k"                        #'paxedit-backward-up
+          :n "g j"                        #'paxedit-backward-end)))
+
+(require 'general)
+(general-evil-setup t)
+
+(nmap
+  ">" (general-key-dispatch 'evil-shift-right
+        "e" 'paxedit-transpose-forward
+        ")" 'sp-forward-slurp-sexp
+        "(" 'sp-backward-barf-sexp
+        "I" 'aspen/insert-at-sexp-end
+        ;; "a" 'grfn/insert-at-form-end
+        ))
+
+(nmap
+  "<" (general-key-dispatch 'evil-shift-left
+        "e" 'paxedit-transpose-backward
+        ")" 'sp-forward-barf-sexp
+        "(" 'sp-backward-slurp-sexp
+        "I" 'aspen/insert-at-sexp-start
+        ;; "a" 'grfn/insert-at-form-start
+        ))
+#+end_src
+
+#+RESULTS:
+
+*** Eval functions
+#+begin_src elisp :tangle yes
+(use-package! predd)
+
+(predd-defmulti eval-sexp (lambda (form) major-mode))
+
+(predd-defmethod eval-sexp 'clojure-mode (form)
+  (cider-interactive-eval form))
+
+(predd-defmethod eval-sexp 'emacs-lisp-mode (form)
+  (pp-eval-expression form))
+
+(predd-defmulti eval-sexp-region (lambda (_beg _end) major-mode))
+
+(predd-defmethod eval-sexp-region 'clojure-mode (beg end)
+  (cider-interactive-eval nil nil (list beg end)))
+
+(predd-defmethod eval-sexp-region 'emacs-lisp-mode (beg end)
+  (pp-eval-expression (read (buffer-substring beg end))))
+
+(predd-defmulti eval-sexp-region-context (lambda (_beg _end _context) major-mode))
+
+(predd-defmethod eval-sexp-region-context 'clojure-mode (beg end context)
+  (cider--eval-in-context (buffer-substring beg end)))
+
+(defun pp-eval-context-region (beg end context)
+  (interactive "r\nxContext: ")
+  (let* ((inner-expr (read (buffer-substring beg end)))
+         (full-expr (list 'let* context inner-expr)))
+    (pp-eval-expression full-expr)))
+
+(predd-defmethod eval-sexp-region-context 'emacs-lisp-mode (beg end context)
+  (pp-eval-context-region beg end context))
+
+(predd-defmulti preceding-sexp (lambda () major-mode))
+
+(predd-defmethod preceding-sexp 'clojure-mode ()
+  (cider-last-sexp))
+
+(predd-defmethod preceding-sexp 'emacs-lisp-mode ()
+  (elisp--preceding-sexp))
+
+(defun eval-sexp-at-point ()
+  (interactive)
+  (let ((bounds (bounds-of-thing-at-point 'sexp)))
+    (eval-sexp-region (car bounds)
+                      (cdr bounds))))
+
+(defun eval-last-sexp (_)
+  (interactive)
+  (eval-sexp (preceding-sexp)))
+
+;;;
+
+(defun cider-insert-current-sexp-in-repl (&optional arg)
+  "Insert the expression at point in the REPL buffer.
+If invoked with a prefix ARG eval the expression after inserting it"
+  (interactive "P")
+  (cider-insert-in-repl (cider-sexp-at-point) arg))
+
+(evil-define-operator fireplace-send (beg end)
+  (cider-insert-current-sexp-in-repl nil nil (list beg end)))
+
+(defun +clojure-pprint-expr (form)
+  (format "(with-out-str (clojure.pprint/pprint %s))"
+          form))
+
+(defun cider-eval-read-and-print-handler (&optional buffer)
+  "Make a handler for evaluating and reading then printing result in BUFFER."
+  (nrepl-make-response-handler
+   (or buffer (current-buffer))
+   (lambda (buffer value)
+     (let ((value* (read value)))
+       (with-current-buffer buffer
+         (insert
+          (if (derived-mode-p 'cider-clojure-interaction-mode)
+              (format "\n%s\n" value*)
+            value*)))))
+   (lambda (_buffer out) (cider-emit-interactive-eval-output out))
+   (lambda (_buffer err) (cider-emit-interactive-eval-err-output err))
+   '()))
+
+(defun cider-eval-and-replace (beg end)
+  "Evaluate the expression in region and replace it with its result"
+  (interactive "r")
+  (let ((form (buffer-substring beg end)))
+    (cider-nrepl-sync-request:eval form)
+    (kill-region beg end)
+    (cider-interactive-eval
+     (+clojure-pprint-expr form)
+     (cider-eval-read-and-print-handler))))
+
+(defun cider-eval-current-sexp-and-replace ()
+  "Evaluate the expression at point and replace it with its result"
+  (interactive)
+  (apply #'cider-eval-and-replace (cider-sexp-at-point 'bounds)))
+
+;;;
+#+end_src
+
+#+RESULTS:
+: fireplace-eval-context
+*** Eval bindings
+fireplace-esque eval binding
+
+#+begin_src elisp :tangle yes
+(evil-define-operator fireplace-eval (beg end)
+  (eval-sexp-region beg end))
+
+(evil-define-operator fireplace-replace (beg end)
+  (cider-eval-and-replace beg end))
+
+(evil-define-operator fireplace-eval-context (beg end)
+  (eval-sexp-region-context beg end))
+
+(nmap :keymaps 'cider-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'fireplace-eval
+              "p" 'cider-eval-sexp-at-point
+              "c" 'cider-eval-last-sexp
+              "d" 'cider-eval-defun-at-point
+              "r" 'cider-test-run-test)
+        "q" (general-key-dispatch 'fireplace-send
+              "q" 'cider-insert-current-sexp-in-repl
+              "c" 'cider-insert-last-sexp-in-repl)
+        "x" (general-key-dispatch 'fireplace-eval-context
+              "x" 'cider-eval-sexp-at-point-in-context
+              "c" 'cider-eval-last-sexp-in-context)
+        "!" (general-key-dispatch 'fireplace-replace
+              "!" 'cider-eval-current-sexp-and-replace
+              "c" 'cider-eval-last-sexp-and-replace)
+        "y" 'cider-copy-last-result))
+
+;;;
+
+(nmap :keymaps 'emacs-lisp-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'fireplace-eval
+              "p" 'eval-sexp-at-point
+              "c" 'eval-last-sexp
+              "d" 'eval-defun
+              "r" 'cider-test-run-test)
+        "x" (general-key-dispatch 'fireplace-eval-context
+              "x" 'cider-eval-sexp-at-point-in-context
+              "c" 'cider-eval-last-sexp-in-context)
+        "!" (general-key-dispatch 'fireplace-replace
+              "!" 'cider-eval-current-sexp-and-replace
+              "c" 'cider-eval-last-sexp-and-replace)
+        "y" 'cider-copy-last-result))
+
+(nmap :keymaps 'sly-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'sly-eval
+              ;; "p" 'eval-sexp-at-point
+              "c" 'sly-eval-last-expression
+              "d" 'sly-eval-defun
+              ;; "r" 'cider-test-run-test
+              )
+        ;; "x" (general-key-dispatch 'fireplace-eval-context
+        ;;       "x" 'cider-eval-sexp-at-point-in-context
+        ;;       "c" 'cider-eval-last-sexp-in-context
+        ;;       )
+        ;; "!" (general-key-dispatch 'fireplace-replace
+        ;;       "!" 'cider-eval-current-sexp-and-replace
+        ;;       "c" 'cider-eval-last-sexp-and-replace)
+        ;; "y" 'cider-copy-last-result
+        ))
+
+#+end_src
+
+#+RESULTS:
+
+** Coerce
+
+#+begin_src elisp :tangle yes
+(use-package! string-inflection
+  :config
+  (nmap "c" (general-key-dispatch 'evil-change
+              "r c" (saving-excursion (string-inflection-lower-camelcase))
+              "r C" (saving-excursion (string-inflection-camelcase))
+              "r m" (saving-excursion (string-inflection-camelcase))
+              "r s" (saving-excursion (string-inflection-underscore))
+              "r u" (saving-excursion (string-inflection-upcase))
+              "r -" (saving-excursion (string-inflection-kebab-case))
+              "r k" (saving-excursion (string-inflection-kebab-case))
+              ;; "r ." (saving-excursion (string-inflection-dot-case))
+              ;; "r ." (saving-excursion (string-inflection-space-case))
+              ;; "r ." (saving-excursion (string-inflection-title-case))
+              )))
+#+end_src
+
+#+RESULTS:
+: t
+
+* Mode-specific config
+** org-mode
+#+begin_src elisp :tangle yes
+(after! org
+  (load! "org-config"))
+#+end_src
+
+#+RESULTS:
+: t
+*** Theme overrides
+
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(org-drawer :foreground ,+solarized-s-base1 :bold t)
+  `(org-block :foreground ,+solarized-s-base00)
+  `(org-meta-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-document-title :foreground ,+solarized-s-base01 :height 1.3)
+  `(org-done :foreground ,+solarized-green)
+  `(org-headline-done :foreground ,+solarized-green)
+  `(org-special-keyword :foreground ,+solarized-s-base1 :bold t)
+  `(org-date :foreground ,+solarized-blue :underline t)
+  `(org-table
+    :foreground ,+solarized-s-base0  ; used to be green, I think I like this better?
+    :italic t)
+  `(org-link :foreground ,+solarized-yellow)
+  `(org-todo :foreground ,+solarized-cyan)
+  `(org-code :foreground ,+solarized-s-base1)
+  `(org-block-begin-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-block-end-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-document-info-keyword :foreground ,+solarized-s-base1 :italic t)
+
+  `(org-level-1 :foreground ,+solarized-red)
+  `(org-level-2 :foreground ,+solarized-green)
+  `(org-level-3 :foreground ,+solarized-blue)
+  `(org-level-4 :foreground ,+solarized-yellow)
+  `(org-level-5 :foreground ,+solarized-cyan)
+  `(org-level-6 :foreground ,+solarized-violet)
+  `(org-level-7 :foreground ,+solarized-magenta)
+  `(org-level-8 :foreground ,+solarized-blue))
+#+end_src
+
+#+RESULTS:
+| doom--customize-themes-h-91 | doom--customize-themes-h-92 | doom--customize-themes-h-93 | doom--customize-themes-h-94 | doom--customize-themes-h-95 | doom--customize-themes-h-96 | doom--customize-themes-h-97 | doom--customize-themes-h-98 | doom--customize-themes-h-99 |
+
+*** Commands
+#+begin_src elisp :tangle yes
+(defun grfn/insert-new-src-block ()
+  (interactive)
+  (let* ((current-src-block (org-element-at-point))
+         (src-block-head (save-excursion
+                           (goto-char (org-element-property
+                                       :begin current-src-block))
+                           (let ((line (thing-at-point 'line t)))
+                             (if (not (s-starts-with? "#+NAME:" (s-trim line)))
+                                 line
+                               (forward-line)
+                               (thing-at-point 'line t)))))
+         (point-to-insert
+          (if-let (results-loc (org-babel-where-is-src-block-result))
+              (save-excursion
+                (goto-char results-loc)
+                (org-element-property
+                 :end
+                 (org-element-at-point)))
+            (org-element-property :end (org-element-at-point)))))
+    (goto-char point-to-insert)
+    (insert "\n")
+    (insert src-block-head)
+    (let ((contents (point-marker)))
+      (insert "\n#+END_SRC\n")
+      (goto-char contents))))
+
+(defun grfn/+org-insert-item (orig direction)
+  (interactive)
+  (if (and (org-in-src-block-p)
+           (equal direction 'below))
+      (grfn/insert-new-src-block)
+    (funcall orig direction)))
+
+(advice-add #'+org--insert-item :around #'grfn/+org-insert-item)
+#+end_src
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:after org
+  :n "C-c C-x C-o" #'org-clock-out
+
+  (:map org-capture-mode-map
+   :n "g RET" #'org-capture-finalize
+   :n "g \\"  #'org-captue-refile)))
+#+end_src
+** magit
+#+begin_src elisp :tangle yes
+(after! magit
+  (map! :map magit-mode-map
+        ;; :n "] ]" #'magit-section-forward
+        ;; :n "[ [" #'magit-section-backward
+        )
+
+  (transient-define-suffix magit-commit-wip ()
+    (interactive)
+    (magit-commit-create '("-m" "wip")))
+
+  (transient-append-suffix
+    #'magit-commit
+    ["c"]
+    (list "W" "Commit WIP" #'magit-commit-wip))
+
+  (transient-define-suffix magit-reset-head-back ()
+    (interactive)
+    (magit-reset-mixed "HEAD~"))
+
+  (transient-define-suffix magit-reset-head-previous ()
+    (interactive)
+    (magit-reset-mixed "HEAD@{1}"))
+
+  (transient-append-suffix
+    #'magit-reset
+    ["f"]
+    (list "b" "Reset HEAD~"    #'magit-reset-head-back))
+  (transient-append-suffix
+    #'magit-reset
+    ["f"]
+    (list "o" "Reset HEAD@{1}" #'magit-reset-head-previous)))
+#+end_src
+
+#+RESULTS:
+
+** elisp
+*** Org config mode
+The minor-mode for *this file*!
+
+#+begin_src elisp :tangle yes
+(after! smartparens
+  (sp-local-pair 'org-config-mode "'" "'" :actions nil)
+  (sp-local-pair 'org-config-mode "`" "`" :actions nil))
+
+(define-minor-mode org-config-mode
+  "Minor-mode for tangled org .el config"
+  :group 'org
+  :lighter "Org-config"
+  :keymap '()
+  (sp-update-local-pairs 'org-config-mode))
+#+end_src
+
+#+RESULTS:
+| keymap |
+
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:map emacs-lisp-mode-map
+  :n "g SPC" #'eval-buffer
+  :n "g RET" (λ! () (ert t)) ))
+#+end_src
+
+#+RESULTS:
+
+** tuareg
+*** Config
+
+#+begin_src elisp :tangle yes
+
+(defun aspen/tuareg-setup ()
+  (setq-local sp-max-pair-length (->> '("begin" "sig" "struct")
+                                      (--map (length it))
+                                      (-max))
+              whitespace-line-column 80))
+
+(add-hook 'tuareg-mode-hook #'aspen/tuareg-setup)
+
+(defun sp-tuareg-post-handler (id action context)
+  (when (equal action 'insert)
+    (save-excursion
+      (insert "x")
+      (newline)
+      (indent-according-to-mode))
+    (delete-char 1)))
+
+(after! smartparens-ml
+  (sp-local-pair 'tuareg-mode "module" "end" :actions nil)
+
+  (dolist (pair-start '("begin" "sig" "struct"))
+    (sp-local-pair 'tuareg-mode
+                   pair-start "end"
+                   :when '(("SPC" "RET" "<evil-ret>"))
+                   :unless '(sp-in-string-p)
+                   :actions '(insert navigate)
+                   :post-handlers '(sp-tuareg-post-handler))))
+nil
+    #+end_src
+
+#+RESULTS:
+
+#+begin_src elisp :tangle yes
+(after! dune-mode
+  (add-hook 'dune-mode-hook 'paxedit-mode))
+#+end_src
+
+#+RESULTS:
+
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:map tuareg-mode-map
+  :n "g RET" (λ! () (compile "dune build @@runtest"))
+  :n "g SPC" #'dune-promote
+  :n "g \\" #'utop
+  :n "g y" #'merlin-locate-type
+  "C-c C-f" (λ! () (compile "dune fmt"))))
+#+end_src
+
+#+RESULTS:
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(tuareg-font-lock-governing-face :foreground ,+solarized-s-base01 :bold t)
+  `(tuareg-font-lock-label-face :foreground ,+solarized-blue)
+  `(tuareg-font-lock-constructor-face :foreground ,+solarized-yellow)
+  `(tuareg-font-lock-operator-face :foreground ,+solarized-red)
+  `(tuareg-font-lock-attribute-face :foreground ,+solarized-red :bold nil)
+  `(tuareg-font-lock-extension-node-face :background nil :inherit 'font-lock-preprocessor-face)
+  `(merlin-eldoc-occurrences-face :background ,+solarized-s-base2)
+  `(merlin-type-face :background ,+solarized-s-base2)
+  `(utop-prompt :foreground ,+solarized-blue)
+  `(utop-frozen :foreground ,+solarized-s-base1 :italic t))
+#+end_src
+
+#+RESULTS:
+| doom--customize-themes-h-30 | doom--customize-themes-h-31 | doom--customize-themes-h-32 | doom--customize-themes-h-42 | doom--customize-themes-h-46 | doom--customize-themes-h-47 | doom--customize-themes-h-64 | doom--customize-themes-h-65 | doom--customize-themes-h-66 | doom--customize-themes-h-67 | doom--customize-themes-h-68 | doom--customize-themes-h-69 |
+
+** clojure
+
+*** Setup
+
+#+begin_src elisp :tangle yes
+(defun clojure-thing-at-point-setup ()
+  (interactive)
+  ;; Used by cider-find-dwim to parse the symbol at point
+  (setq-local
+   thing-at-point-file-name-chars
+   (concat thing-at-point-file-name-chars
+           "><!?")))
+
+(defun +grfn/clojure-setup ()
+  ;; (flycheck-select-checker 'clj-kondo)
+  (require 'flycheck)
+  (push 'clojure-cider-kibit flycheck-disabled-checkers)
+  (push 'clojure-cider-eastwood flycheck-disabled-checkers)
+  (push 'clojure-cider-typed flycheck-disabled-checkers)
+  )
+
+(after! clojure-mode
+  (define-clojure-indent
+    (PUT 2)
+    (POST 2)
+    (GET 2)
+    (PATCH 2)
+    (DELETE 2)
+    (context 2)
+    (checking 3)
+    (match 1)
+    (domonad 0)
+    (describe 1)
+    (before 1)
+    (it 2))
+
+  (add-hook 'clojure-mode-hook #'clojure-thing-at-point-setup)
+  (add-hook 'clojure-mode-hook #'+grfn/clojure-setup))
+
+(use-package! flycheck-clojure
+  ;; :disabled t
+  :after (flycheck cider)
+  :config
+  (flycheck-clojure-setup))
+
+(after! clj-refactor
+  (setq cljr-magic-requires :prompt
+        cljr-clojure-test-declaration "[clojure.test :refer :all]"
+        cljr-cljc-clojure-test-declaration"#?(:clj [clojure.test :refer :all]
+:cljs [cljs.test :refer-macros [deftest is testing]])"
+        )
+  (add-to-list
+   'cljr-magic-require-namespaces
+   '("s" . "clojure.spec.alpha")))
+
+(set-popup-rule! "^\\*cider-test-report" :size 0.4)
+nil
+#+end_src
+
+#+RESULTS:
+
+*** Commands
+
+#+begin_src elisp :tangle yes
+(defun grfn/run-clj-or-cljs-test ()
+  (interactive)
+  (message "Running tests...")
+  (cl-case (cider-repl-type-for-buffer)
+    (cljs
+     (cider-interactive-eval
+      "(with-out-str (cljs.test/run-tests))"
+      (nrepl-make-response-handler
+       (current-buffer)
+       (lambda (_ value)
+         (with-output-to-temp-buffer "*cljs-test-results*"
+           (print
+            (->> value
+                 (s-replace "\"" "")
+                 (s-replace "\\n" "\n")))))
+       nil nil nil)))
+    (('clj 'multi)
+     (funcall-interactively
+      #'cider-test-run-ns-tests
+      nil))))
+
+(defun cider-copy-last-result ()
+  (interactive)
+  (cider-interactive-eval
+   "*1"
+   (nrepl-make-response-handler
+    (current-buffer)
+    (lambda (_ value)
+      (kill-new value)
+      (message "Copied last result (%s) to clipboard"
+               (if (= (length value) 1) "1 char"
+                 (format "%d chars" (length value)))))
+    nil nil nil)))
+
+#+end_src
+
+#+RESULTS:
+: cider-copy-last-result
+
+*** Bindings
+
+
+#+begin_src elisp :tangle yes
+(map!
+ (:after
+  clojure-mode
+  (:map clojure-mode-map
+   :n "] f" 'forward-sexp
+   :n "[ f" 'backward-sexp))
+
+ (:after
+  cider-mode
+  (:map cider-mode-map
+   :n "g SPC" 'cider-eval-buffer
+   :n "g \\"  'cider-switch-to-repl-buffer
+   :n "K"     'cider-doc
+   :n "g K"   'cider-apropos
+   :n "g d"   'cider-find-dwim
+   :n "C-w ]" 'cider-find-dwim-other-window
+   ;; :n "g RET" 'cider-test-run-ns-tests
+   :n "g RET" 'grfn/run-clj-or-cljs-test
+   :n "g r" #'cljr-rename-symbol
+
+   "C-c C-r r" 'cljr-add-require-to-ns
+   "C-c C-r i" 'cljr-add-import-to-ns
+
+   (:localleader
+    ;; :desc "Inspect last result" :n "i" 'cider-inspect-last-result
+    ;; :desc "Search for documentation" :n "h s" 'cider-apropos-doc
+    :desc "Add require to ns" :n "n r" 'cljr-add-require-to-ns
+    :desc "Add import to ns" :n "n i" 'cljr-add-import-to-ns))
+  (:map cider-repl-mode-map
+   :n "g \\" 'cider-switch-to-last-clojure-buffer)))
+ #+end_src
+
+ #+RESULTS:
+
+** rust
+*** Config
+#+begin_src elisp
+(defun aspen/rust-setup ()
+  (interactive)
+  (+evil-embrace-angle-bracket-modes-hook-h)
+  (setq-local whitespace-line-column 100
+              fill-column 100)
+  (eglot-inlay-hints-mode -1)
+  (setq lsp-rust-analyzer-cargo-watch-command "clippy"
+        rustic-format-trigger 'on-save
+        ))
+
+(add-hook 'rust-mode-hook #'aspen/rust-setup)
+#+end_src
+
+#+RESULTS:
+| doom-modeline-env-setup-rust | aspen/rust-setup |
+
+*** Bindings
+
+*** Theme overrides
+#+begin_src elisp
+(custom-set-faces!
+  `(rust-unsafe :foreground ,+solarized-red))
+#+end_src
+
+#+RESULTS:
+| doom--customize-themes-h-30 | doom--customize-themes-h-31 | doom--customize-themes-h-32 | doom--customize-themes-h-33 | doom--customize-themes-h-54 |
+
+* Email
+#+begin_src elisp :tangle yes
+(after! notmuch
+  (setq notmuch-saved-searches
+        '((:name "inbox" :query "tag:inbox tag:important not tag:trash" :key "i")
+          (:name "flagged" :query "tag:flagged" :key "f")
+          (:name "sent" :query "tag:sent" :key "s")
+          (:name "drafts" :query "tag:draft" :key "d")
+
+          (:name "work" :query "tag:inbox and tag:important and path:work/**"
+                 :key "w")
+          (:name "personal" :query "tag:inbox and tag:important and path:personal/**"
+                 :key "p"))
+        message-send-mail-function 'message-send-mail-with-sendmail
+        message-sendmail-f-is-evil 't
+        message-sendmail-envelope-from 'header
+        message-sendmail-extra-arguments '("--read-envelope-from")))
+#+end_src
+
+#+RESULTS:
+| --read-envelope-from |
+
+** Bindings
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:leader
+  :desc "Email" :n "o m" #'notmuch-jump-search
+  :desc "Search email" "s M" #'consult-notmuch))
+#+end_src
+
+#+RESULTS:
+: notmuch-jump-search
+
+
+** Theme
+
+#+begin_src emacs-lisp :tangle yes
+(custom-set-faces!
+  `(notmuch-message-summary-face
+    :background ,+solarized-halfway-highlight))
+#+end_src
+
+#+RESULTS:
+| doom--customize-themes-h-91 | doom--customize-themes-h-92 | doom--customize-themes-h-93 | doom--customize-themes-h-94 | doom--customize-themes-h-95 | doom--customize-themes-h-96 | doom--customize-themes-h-97 | doom--customize-themes-h-98 | doom--customize-themes-h-99 | doom--customize-themes-h-100 |
+
+* Misc
+** Make underscores word chars
+#+begin_src elisp :tangle yes
+(modify-syntax-entry ?_ "w")
+#+end_src
+
+#+RESULTS:
+
+** Matchit
+#+begin_src elisp :tangle yes
+(use-package! evil-matchit)
+#+end_src
+** IRC
+*** Connecting to IRC
+
+#+begin_src elisp :tangle yes
+(defvar irc-servers
+  '("hackint"
+    "libera"))
+
+(defun irc-connect (server)
+  (interactive
+   (list (completing-read "Server: " irc-servers)))
+  (let ((pw (-> (shell-command-to-string
+                 (format "pass irccloud/%s" server))
+                (s-trim)
+                (s-lines)
+                (-last-item)))
+        (gnutls-verify-error nil))
+    (erc-tls :server "bnc.irccloud.com"
+             :port 6697
+             :nick "aspen"
+             :password (concat "bnc@"
+                               (s-trim (shell-command-to-string "hostname"))
+                               ":"
+                               pw))))
+
+(defun aspen/switch-to-erc-buffer-or-connect ()
+  (interactive)
+  (if (functionp 'erc-switch-to-buffer)
+      (call-interactively #'erc-switch-to-buffer)
+    (call-interactively #'irc-connect)))
+#+end_src
+
+#+RESULTS:
+: aspen/switch-to-erc-buffer-or-connect
+
+#+begin_src elisp :tangle yes
+(map! :leader "o I" #'irc-connect
+      :leader "o i" #'aspen/switch-to-erc-buffer-or-connect)
+#+end_src
+
+#+RESULTS:
+: aspen/switch-to-erc-buffer-or-connect
+
+*** IRC alerts
+#+begin_src elisp :tangle yes
+(use-package! alert)
+
+(defgroup erc-alert nil
+  "Alert me using alert.el for important ERC messages"
+  :group 'erc)
+
+(defcustom erc-noise-regexp
+  "\\(Logging in:\\|Signing off\\|You're now away\\|Welcome back\\)"
+  "This regexp matches unwanted noise."
+  :type 'regexp
+  :group 'erc)
+
+(setq tvl-enabled? t)
+
+(defun disable-tvl-notifications ()
+  (interactive)
+  (setq tvl-enabled? nil))
+
+(defun enable-tvl-notifications ()
+  (interactive)
+  (setq tvl-enabled? t))
+
+(defun erc-alert-important-p (info)
+  (let ((message (plist-get info :message))
+        (erc-message (-> info (plist-get :data) (plist-get :message)))
+        (erc-channel (-> info (plist-get :data) (plist-get :channel))))
+    (and erc-message
+         (not (or (string-match "^\\** *Users on #" message)
+                  (string-match erc-noise-regexp
+                                message)))
+         (or (and tvl-enabled?
+                  (string-equal erc-channel "#tvl"))
+             (string-match "grfn" message)))))
+
+(comment
+ last-info
+ erc-noise-regexp
+ (setq tvl-enabled? nil)
+ )
+
+(defun my-erc-hook (&optional match-type nick message)
+  "Shows a notification, when user's nick was mentioned.
+If the buffer is currently not visible, makes it sticky."
+  (setq last-message message)
+  (if (or (null match-type) (not (eq match-type 'fool)))
+      (let (alert-log-messages)
+        (alert (or message (buffer-string))
+               :severity (if (string-match "grfn" (or message ""))
+                             'high 'low)
+               :title (or nick (buffer-name))
+               :data `(:message ,(or message (buffer-string))
+                                :channel ,(or nick (buffer-name)))))))
+
+(add-hook 'erc-text-matched-hook 'my-erc-hook)
+(add-hook 'erc-insert-modify-hook 'my-erc-hook)
+
+(defun my-erc-define-alerts (&rest ignore)
+  ;; Unless the user has recently typed in the ERC buffer, highlight the fringe
+  (alert-add-rule
+   :status   '(buried visible idle)
+   :severity '(moderate high urgent)
+   :mode     'erc-mode
+   :predicate
+   #'(lambda (info)
+       (and (not (eq (current-buffer) (plist-get info :buffer)))
+            (string-match "grfn:" (plist-get info :message))))
+   :persistent
+   #'(lambda (info)
+       ;; If the buffer is buried, or the user has been idle for
+       ;; `alert-reveal-idle-time' seconds, make this alert
+       ;; persistent.  Normally, alerts become persistent after
+       ;; `alert-persist-idle-time' seconds.
+       (memq (plist-get info :status) '(buried idle)))
+   :style 'message
+   :continue t)
+
+  (alert-add-rule
+   :status 'buried
+   :mode   'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'libnotify
+   :append t)
+
+  (alert-add-rule
+   :status 'buried
+   :mode   'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'message
+   :append t)
+
+  (alert-add-rule
+   :mode 'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'log
+   :append t)
+
+  (alert-add-rule :mode 'erc-mode :style 'ignore :append t))
+
+(add-hook 'erc-connect-pre-hook 'my-erc-define-alerts)
+#+end_src
+
+#+RESULTS:
+| my-erc-define-alerts |
+
+*** Don't send ~:q~, etc, to the server
+#+begin_src elisp :tangle yes
+(defun fix-irc-message (msg)
+  (let ((msg (s-trim msg)))
+    (if (string-equal msg ":q") "" msg)))
+(advice-add #'erc-user-input :filter-return #'fix-irc-message)
+#+end_src
+
+#+RESULTS:
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(erc-button :foreground ,+solarized-blue))
+#+end_src
+
+#+RESULTS:
+| doom--customize-themes-h-30 | doom--customize-themes-h-31 | doom--customize-themes-h-32 | doom--customize-themes-h-43 | doom--customize-themes-h-47 | doom--customize-themes-h-48 | doom--customize-themes-h-49 | doom--customize-themes-h-50 | doom--customize-themes-h-51 | doom--customize-themes-h-52 | doom--customize-themes-h-53 | doom--customize-themes-h-54 | doom--customize-themes-h-56 | doom--customize-themes-h-57 | doom--customize-themes-h-58 | doom--customize-themes-h-59 | doom--customize-themes-h-60 | doom--customize-themes-h-61 | doom--customize-themes-h-62 | doom--customize-themes-h-63 | doom--customize-themes-h-64 |
+
+*** TODO Nick rainbow colors
+Stole this from https://github.com/jtdaugherty/emacs-config/blob/master/common/erc-nick-colors.el.
+
+IT doesn't work though :(
+
+#+begin_src elisp :tangle yes
+(setq nick-face-list '())
+
+;; Define the list of colors to use when coloring IRC nicks.
+(setq-default erc-colors-list (list +solarized-yellow
+                                    +solarized-orange
+                                    +solarized-red
+                                    +solarized-magenta
+                                    +solarized-violet
+                                    +solarized-blue
+                                    +solarized-cyan
+                                    +solarized-green))
+
+(defun build-nick-face-list ()
+  "build-nick-face-list builds a list of new faces using the
+foreground colors specified in erc-colors-list.  The nick faces
+created here will be used to format IRC nicks."
+  (let ((i -1))
+    (setq nick-face-list
+          (mapcar
+           (lambda (COLOR)
+             (setq i (1+ i))
+             (list (custom-declare-face
+                    (make-symbol (format "erc-nick-face-%d" i))
+                    (list (list t (list :foreground COLOR)))
+                    (format "Nick face %d" i))))
+           erc-colors-list))))
+
+(defun erc-insert-nick-colors ()
+  "This insert-modify hook looks for nicks in new messages and
+computes md5(nick) and uses substring(md5_value, 0, 4) mod (length
+nick-face-list) to index the face list and produce the same face for a
+given nick each time it is seen.  We get a lot of collisions this way,
+unfortunately, but it's better than some other methods I tried.
+Additionally, if you change the order or size of the erc-colors-list,
+you'll change the colors used for nicks."
+  (if (null nick-face-list) (build-nick-face-list))
+  (save-excursion
+    (goto-char (point-min))
+    (if (looking-at "<\\([^>]*\\)>")
+        (let ((nick (match-string 1)))
+          (put-text-property (match-beginning 1) (match-end 1)
+                             'face (nth
+                                    (mod (string-to-number
+                                          (substring (md5 nick) 0 4) 16)
+                                         (length nick-face-list))
+                                    nick-face-list))))))
+
+;; This adds the ERC message insert hook.
+(add-hook 'erc-insert-modify-hook 'erc-insert-nick-colors)
+#+end_src
+
+#+RESULTS:
+| erc-insert-nick-colors | erc-controls-highlight | erc-fill | my-erc-hook | erc-button-add-buttons | erc-match-message | erc-add-timestamp |
+
+* Hacks
+Not having this breaks elisp documentation :(
+#+begin_src elisp :tangle yes
+(defvar elisp-demos-user-files nil)
+#+end_src
+
+#+RESULTS:
+: elisp-demos-user-files
diff --git a/users/aspen/emacs/init.el b/users/aspen/emacs/init.el
new file mode 100644
index 0000000000..2fe2316753
--- /dev/null
+++ b/users/aspen/emacs/init.el
@@ -0,0 +1,197 @@
+;;; init.el -*- lexical-binding: t; -*-
+
+;; This file controls what Doom modules are enabled and what order they load
+;; in. Remember to run 'doom sync' after modifying it!
+
+;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
+;;      documentation. There you'll find a link to Doom's Module Index where all
+;;      of our modules are listed, including what flags they support.
+
+;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
+;;      'C-c c k' for non-vim users) to view its documentation. This works on
+;;      flags as well (those symbols that start with a plus).
+;;
+;;      Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
+;;      directory (for easy access to its source code).
+
+(doom! :input
+       ;;bidi              ; (tfel ot) thgir etirw uoy gnipleh
+       ;;chinese
+       ;;japanese
+       ;;layout            ; auie,ctsrnm is the superior home row
+
+       :completion
+       company             ; the ultimate code completion backend
+       ;;helm              ; the *other* search engine for love and life
+       ;;ido               ; the other *other* search engine...
+       ;;ivy               ; a search engine for love and life
+       vertico           ; the search engine of the future
+
+       :ui
+       ;;deft              ; notational velocity for Emacs
+       doom              ; what makes DOOM look the way it does
+       ;; doom-dashboard    ; a nifty splash screen for Emacs
+       ;;doom-quit         ; DOOM quit-message prompts when you quit Emacs
+       (emoji +unicode)  ; 🙂
+       hl-todo           ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
+       ;;hydra
+       ;;indent-guides     ; highlighted indent columns
+       ;;ligatures         ; ligatures and symbols to make your code pretty again
+       ;;minimap           ; show a map of the code on the side
+       modeline          ; snazzy, Atom-inspired modeline, plus API
+       ;;nav-flash         ; blink cursor line after big motions
+       ;;neotree           ; a project drawer, like NERDTree for vim
+       ophints           ; highlight the region an operation acts on
+       (popup +defaults)   ; tame sudden yet inevitable temporary windows
+       ;;tabs              ; a tab bar for Emacs
+       ;;treemacs          ; a project drawer, like neotree but cooler
+       ;;unicode           ; extended unicode support for various languages
+       (vc-gutter +pretty) ; vcs diff in the fringe
+       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
+       ;;window-select     ; visually switch windows
+       workspaces        ; tab emulation, persistence & separate workspaces
+       ;;zen               ; distraction-free coding or writing
+
+       :editor
+       (evil +everywhere); come to the dark side, we have cookies
+       file-templates    ; auto-snippets for empty files
+       fold              ; (nigh) universal code folding
+       (format +onsave)  ; automated prettiness
+       ;;god               ; run Emacs commands without modifier keys
+       ;;lispy             ; vim for lisp, for people who don't like vim
+       ;;multiple-cursors  ; editing in many places at once
+       ;;objed             ; text object editing for the innocent
+       ;;parinfer          ; turn lisp into python, sort of
+       ;;rotate-text       ; cycle region at point between text candidates
+       snippets          ; my elves. They type so I don't have to
+       word-wrap         ; soft wrapping with language-aware indent
+
+       :emacs
+       dired             ; making dired pretty [functional]
+       electric          ; smarter, keyword-based electric-indent
+       ;;ibuffer         ; interactive buffer management
+       undo              ; persistent, smarter undo for your inevitable mistakes
+       vc                ; version-control and Emacs, sitting in a tree
+
+       :term
+       ;;eshell            ; the elisp shell that works everywhere
+       ;;shell             ; simple shell REPL for Emacs
+       ;;term              ; basic terminal emulator for Emacs
+       vterm             ; the best terminal emulation in Emacs
+
+       :checkers
+       syntax              ; tasing you for every semicolon you forget
+       (spell +flyspell) ; tasing you for misspelling mispelling
+       ;;grammar           ; tasing grammar mistake every you make
+
+       :tools
+       ;;ansible
+       ;;biblio            ; Writes a PhD for you (citation needed)
+       ;;debugger          ; FIXME stepping through code, to help you add bugs
+       direnv
+       docker
+       ;;editorconfig      ; let someone else argue about tabs vs spaces
+       ;;ein               ; tame Jupyter notebooks with emacs
+       (eval +overlay)     ; run code, run (also, repls)
+       ;;gist              ; interacting with github gists
+       lookup              ; navigate your code and its documentation
+       (lsp +eglot)               ; M-x vscode
+       magit             ; a git porcelain for Emacs
+       ;;make              ; run make tasks from Emacs
+       pass              ; password manager for nerds
+       ;;pdf               ; pdf enhancements
+       ;;prodigy           ; FIXME managing external services & code builders
+       ;;rgb               ; creating color strings
+       ;;taskrunner        ; taskrunner for all your projects
+       terraform         ; infrastructure as code
+       ;;tmux              ; an API for interacting with tmux
+       ;;tree-sitter       ; syntax and parsing, sitting in a tree...
+       ;;upload            ; map local to remote projects via ssh/ftp
+
+       :os
+       (:if IS-MAC macos)  ; improve compatibility with macOS
+       ;;tty               ; improve the terminal Emacs experience
+
+       :lang
+       agda              ; types of types of types of types...
+       ;;beancount         ; mind the GAAP
+       ;;(cc +lsp)         ; C > C++ == 1
+       clojure           ; java with a lisp
+       common-lisp       ; if you've seen one lisp, you've seen them all
+       ;;coq               ; proofs-as-programs
+       ;;crystal           ; ruby at the speed of c
+       ;;csharp            ; unity, .NET, and mono shenanigans
+       data              ; config/data formats
+       ;;(dart +flutter)   ; paint ui and not much else
+       ;;dhall
+       ;;elixir            ; erlang done right
+       ;;elm               ; care for a cup of TEA?
+       emacs-lisp        ; drown in parentheses
+       ;;erlang            ; an elegant language for a more civilized age
+       ;;ess               ; emacs speaks statistics
+       ;;factor
+       ;;faust             ; dsp, but you get to keep your soul
+       ;;fortran           ; in FORTRAN, GOD is REAL (unless declared INTEGER)
+       ;;fsharp            ; ML stands for Microsoft's Language
+       ;;fstar             ; (dependent) types and (monadic) effects and Z3
+       ;;gdscript          ; the language you waited for
+       ;;(go +lsp)         ; the hipster dialect
+       ;;(graphql +lsp)    ; Give queries a REST
+       (haskell +lsp)    ; a language that's lazier than I am
+       ;;hy                ; readability of scheme w/ speed of python
+       ;;idris             ; a language you can depend on
+       json              ; At least it ain't XML
+       ;;(java +lsp)       ; the poster child for carpal tunnel syndrome
+       ;;javascript        ; all(hope(abandon(ye(who(enter(here))))))
+       ;;julia             ; a better, faster MATLAB
+       ;;kotlin            ; a better, slicker Java(Script)
+       ;;latex             ; writing papers in Emacs has never been so fun
+       ;;lean              ; for folks with too much to prove
+       ;;ledger            ; be audit you can be
+       ;;lua               ; one-based indices? one-based indices
+       markdown          ; writing docs for people to ignore
+       ;;nim               ; python + lisp at the speed of c
+       nix               ; I hereby declare "nix geht mehr!"
+       ocaml             ; an objective camel
+       (org               ; organize your plain life in plain text
+        +gnuplot
+        +present
+        +pretty)
+       ;;php               ; perl's insecure younger brother
+       ;;plantuml          ; diagrams for confusing people more
+       ;;purescript        ; javascript, but functional
+       python            ; beautiful is better than ugly
+       ;;qt                ; the 'cutest' gui framework ever
+       ;;racket            ; a DSL for DSLs
+       ;;raku              ; the artist formerly known as perl6
+       ;;rest              ; Emacs as a REST client
+       ;;rst               ; ReST in peace
+       ;;(ruby +rails)     ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
+       (rust +lsp)       ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
+       ;;scala             ; java, but good
+       ;;(scheme +guile)   ; a fully conniving family of lisps
+       sh                ; she sells {ba,z,fi}sh shells on the C xor
+       ;;sml
+       ;;solidity          ; do you need a blockchain? No.
+       ;;swift             ; who asked for emoji variables?
+       ;;terra             ; Earth and Moon in alignment for performance.
+       web               ; the tubes
+       yaml              ; JSON, but readable
+       ;;zig               ; C, but simpler
+
+       :email
+       ;;(mu4e +org +gmail)
+       notmuch
+       ;;(wanderlust +gmail)
+
+       :app
+       ;;calendar
+       ;;emms
+       ;;everywhere        ; *leave* Emacs!? You must be joking
+       irc               ; how neckbeards socialize
+       ;;(rss +org)        ; emacs as an RSS reader
+       ;;twitter           ; twitter client https://twitter.com/vnought
+
+       :config
+       literate
+       (default +bindings +smartparens))
diff --git a/users/aspen/emacs/org-config.el b/users/aspen/emacs/org-config.el
new file mode 100644
index 0000000000..df93142b0f
--- /dev/null
+++ b/users/aspen/emacs/org-config.el
@@ -0,0 +1,141 @@
+;;; org-config.el -*- lexical-binding: t; -*-
+
+(defun +aspen/org-setup ()
+  (setq-local truncate-lines -1)
+  (display-line-numbers-mode -1)
+  (line-number-mode -1)
+  (when-let*
+      ((path (buffer-file-name))
+       (fn (file-name-nondirectory path))
+       (equal (string-equal fn "config.org")))
+    (paxedit-mode 1)
+    (display-line-numbers-mode 1)
+    (flyspell-mode -1)
+    (org-config-mode 1)))
+
+(add-hook 'org-mode-hook #'+aspen/org-setup 50)
+
+(defun notes-file (f)
+  (concat org-directory (if (string-prefix-p "/" f) "" "/") f))
+
+(defun aspen/org-project-tag->key (tag)
+  (s-replace-regexp "^project__" "" tag))
+
+(defun aspen/org-project-tag->name (tag)
+  (s-titleized-words
+   (s-join " " (s-split "_" (aspen/org-project-tag->key tag)))))
+
+(defun aspen/org-project-tag->keys (tag)
+  (s-join "" (cons "p"
+                   (-map (lambda (s) (substring-no-properties s 0 1))
+                         (s-split "_" (aspen/org-project-tag->key tag))))))
+
+(defun aspen/org-projects->agenda-commands (project-tags)
+  (cl-loop for tag in project-tags
+           collect `(,(aspen/org-project-tag->keys tag)
+                     ,(aspen/org-project-tag->name tag)
+                     tags-todo
+                     ,tag)))
+
+(defun aspen/org-projects ()
+  (cl-loop for (tag) in
+           (org-global-tags-completion-table
+            (directory-files-recursively "~/notes" "\\.org$"))
+           when (s-starts-with-p "project__" tag)
+           collect tag))
+
+(comment
+ (aspen/org-projects->agenda-commands (aspen/org-projects))
+ )
+
+(setq
+ org-directory (expand-file-name "~/notes")
+ +org-dir (expand-file-name "~/notes")
+ org-default-notes-file (concat org-directory "/inbox.org")
+ +org-default-todo-file (concat org-directory "/inbox.org")
+ org-agenda-files (directory-files-recursively
+                   "~/notes" "\\.org$")
+ org-refile-targets '((org-agenda-files :maxlevel . 3))
+ org-outline-path-complete-in-steps nil
+ org-refile-use-outline-path t
+ org-file-apps `((auto-mode . emacs)
+                 (,(rx (or (and "." (optional "x") (optional "htm") (optional "l") buffer-end)
+                           (and buffer-start "http" (optional "s") "://")))
+                  . "firefox %s")
+                 (,(rx ".pdf" buffer-end) . "apvlv %s")
+                 (,(rx "." (or "png"
+                               "jpg"
+                               "jpeg"
+                               "gif"
+                               "tif"
+                               "tiff")
+                       buffer-end)
+                  . "feh %s"))
+ org-log-done 'time
+ org-archive-location "~/notes/trash::* From %s"
+ org-cycle-separator-lines 2
+ org-hidden-keywords '(title)
+ org-tags-column -130
+ org-ellipsis "…"
+ org-imenu-depth 9
+ org-capture-templates
+ `(("t" "Todo" entry
+    (file +org-default-todo-file)
+    "* TODO %?\n%i"
+    :kill-buffer t)
+
+   ("m" "Email" entry
+    (file +org-default-todo-file)
+    "* TODO [[%L][%:subject]] :email:\n%i")
+
+   ("n" "Notes" entry
+    (file +org-default-todo-file)
+    "* %U %?\n%i"
+    :prepend t
+    :kill-buffer t)
+
+   ("c" "Task note" entry
+    (clock)
+    "* %U %?\n%i[%l[Context]]\n"
+    :kill-buffer t
+    :unnarrowed t)
+
+   ("p" "Projects")
+   ("px" "Xanthous" entry
+    (file+headline ,(notes-file "xanthous.org") "Backlog")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pt" "Tvix" entry
+    (file+headline ,(notes-file "tvix.org") "Tvix TODO")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pw" "Windtunnel" entry
+    (file+headline ,(notes-file "windtunnel.org") "Inbox")
+    "* TODO %i%?\nContext: %a\nIn task: %K")
+   )
+
+ org-capture-templates-contexts
+ `(("px" ((in-file . "/home/aspen/code/depot/users/aspen/xanthous/.*")))
+   ("e" ((in-mode . "notmuch-show-mode"))))
+
+ org-deadline-warning-days 1
+ org-agenda-skip-scheduled-if-deadline-is-shown 'todo
+ org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
+                     (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
+ org-agenda-custom-commands
+ `(("i" "Inbox" tags "inbox")
+   ("r" "Running jobs" todo "RUNNING")
+   ("w" "@Work" tags-todo "@work")
+   ("n" . "Next...")
+   ("nw" "Next @Work" tags-todo "@work&next")
+   ("nt" "Next tooling" tags-todo "tooling")
+
+   ("p" . "Project...")
+   ;; ,@(aspen/org-projects->agenda-commands (aspen/org-projects))
+   )
+
+ org-agenda-dim-blocked-tasks nil
+ org-enforce-todo-dependencies nil
+
+ org-babel-clojure-backend 'cider)
+
+(setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
+(setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
diff --git a/users/aspen/emacs/packages.el b/users/aspen/emacs/packages.el
new file mode 100644
index 0000000000..cad4cc9553
--- /dev/null
+++ b/users/aspen/emacs/packages.el
@@ -0,0 +1,12 @@
+;; -*- no-byte-compile: t; -*-
+;;; $DOOMDIR/packages.el
+
+(package! dash)
+(package! paxedit)
+(package! predd
+  :recipe (:host github :repo "skeeto/predd"))
+(package! alert)
+(package! flycheck-clojure)
+(package! evil-matchit)
+(package! string-inflection)
+(package! protobuf-mode)
diff --git a/users/aspen/emacs/snippets/haskell-mode/annotation b/users/aspen/emacs/snippets/haskell-mode/annotation
new file mode 100644
index 0000000000..8a2854d759
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/annotation
@@ -0,0 +1,5 @@
+# key: ann
+# name: annotation
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# ANN ${1:module} ("${2:HLint: ignore ${3:Reduce duplication}}" :: String) #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/benchmark-module b/users/aspen/emacs/snippets/haskell-mode/benchmark-module
new file mode 100644
index 0000000000..cbb1646e41
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/benchmark-module
@@ -0,0 +1,26 @@
+# key: bench
+# name: benchmark-module
+# expand-env: ((yas-indent-line (quote fixed)))
+# --
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (cl-search "bench/" name)
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`} ( benchmark, main ) where
+--------------------------------------------------------------------------------
+import Bench.Prelude
+--------------------------------------------------------------------------------
+import ${1:$(s-chop-suffix "Bench" yas-text)}
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain [benchmark]
+
+--------------------------------------------------------------------------------
+
+benchmark :: Benchmark
+benchmark = bgroup "${1:$(->> yas-text (s-chop-suffix "Bench") (s-split ".") -last-item)}" [bench "something dumb" $ nf (1 +) (1 :: Int)]
diff --git a/users/aspen/emacs/snippets/haskell-mode/header b/users/aspen/emacs/snippets/haskell-mode/header
new file mode 100644
index 0000000000..fdd8250d86
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/header
@@ -0,0 +1,5 @@
+# key: hh
+# name: header
+# expand-env: ((yas-indent-line 'fixed))
+# --
+--------------------------------------------------------------------------------$2
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator b/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator
new file mode 100644
index 0000000000..68863f7054
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator
@@ -0,0 +1,8 @@
+# key: gen
+# name: Hedgehog Generator
+# expand-env: ((yas-indent-line (quote fixed)))
+# --
+gen${1:Foo} :: Gen $1
+gen$1 = do
+  $2
+  pure $1{..}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hedgehog-property b/users/aspen/emacs/snippets/haskell-mode/hedgehog-property
new file mode 100644
index 0000000000..bf39a2a3ee
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hedgehog-property
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: Hedgehog Property
+# key: hprop
+# expand-env: ((yas-indent-line 'fixed))
+# --
+hprop_${1:somethingIsAlwaysTrue} :: Property
+hprop_$1 = property $ do
+  ${2:x} <- forAll ${3:Gen.int $ Range.linear 1 100}
+  ${4:x === x}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hlint b/users/aspen/emacs/snippets/haskell-mode/hlint
new file mode 100644
index 0000000000..f25a9b8d40
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hlint
@@ -0,0 +1,8 @@
+# -*- mode: snippet -*-
+# name: hlint
+# uuid: hlint
+# expand-env: ((yas-indent-line 'fixed))
+# key: hlint
+# condition: t
+# --
+{-# ANN module ("Hlint: ignore $1" :: String) #- }
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/import-i b/users/aspen/emacs/snippets/haskell-mode/import-i
new file mode 100644
index 0000000000..4a7fca2c2f
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/import-i
@@ -0,0 +1,4 @@
+# key: i
+# name: import-i
+# --
+import           ${1:Prelude}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/inl b/users/aspen/emacs/snippets/haskell-mode/inl
new file mode 100644
index 0000000000..6e17b83d71
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/inl
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: inl
+# key: inl
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# INLINE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/inline b/users/aspen/emacs/snippets/haskell-mode/inline
new file mode 100644
index 0000000000..1beafbe50b
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/inline
@@ -0,0 +1,5 @@
+# key: inline
+# name: inline
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# INLINE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/language pragma b/users/aspen/emacs/snippets/haskell-mode/language pragma
new file mode 100644
index 0000000000..6f84720f45
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/language pragma
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: language pragma
+# key: lang
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# LANGUAGE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/lens.field b/users/aspen/emacs/snippets/haskell-mode/lens.field
new file mode 100644
index 0000000000..b22ea3d2e8
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/lens.field
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: lens.field
+# key: lens
+# expand-env: ((yas-indent-line 'fixed))
+# --
+${1:field} :: Lens' ${2:Source} ${3:Target}
+$1 = lens _${4:sourceField} $ \\${2:$(-> yas-text s-word-initials s-downcase)} ${4:$(-> yas-text s-word-initials s-downcase)} -> ${2:$(-> yas-text s-word-initials s-downcase)} { _$4 = ${4:$(-> yas-text s-word-initials s-downcase)} }
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/module b/users/aspen/emacs/snippets/haskell-mode/module
new file mode 100644
index 0000000000..4554d33f9b
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/module
@@ -0,0 +1,32 @@
+# -*- mode: snippet -*-
+# key: module
+# name: module
+# condition: (= (length "module") (current-column))
+# expand-env: ((yas-indent-line 'fixed))
+# contributor: Luke Hoersten <luke@hoersten.org>
+# --
+--------------------------------------------------------------------------------
+-- |
+-- Module      : $1
+-- Description : $2
+-- Maintainer  : Griffin Smith <grfn@urbint.com>
+-- Maturity    : ${3:Draft, Usable, Maintained, OR MatureAF}
+--
+-- $4
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (or (cl-search "src/" name)
+                             (cl-search "test/" name))
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`}
+  (
+  ) where
+--------------------------------------------------------------------------------
+import Prelude
+--------------------------------------------------------------------------------
+
+$0
diff --git a/users/aspen/emacs/snippets/haskell-mode/shut up, hlint b/users/aspen/emacs/snippets/haskell-mode/shut up, hlint
new file mode 100644
index 0000000000..fccff1d66f
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/shut up, hlint
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: shut up, hlint
+# key: dupl
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/test-group b/users/aspen/emacs/snippets/haskell-mode/test-group
new file mode 100644
index 0000000000..bf6a66f8a3
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/test-group
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: test-group
+# uuid: test-group
+# key: testGroup
+# condition: t
+# --
+testGroup "${1:name}"
+[ $0
+]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/test-module b/users/aspen/emacs/snippets/haskell-mode/test-module
new file mode 100644
index 0000000000..036b0ae998
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/test-module
@@ -0,0 +1,27 @@
+# -*- mode: snippet -*-
+# name: test-module
+# key: test
+# expand-env: ((yas-indent-line 'fixed))
+# --
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (cl-search "test/" name)
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`} (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+--------------------------------------------------------------------------------
+import           ${1:$(s-chop-suffix "Spec" yas-text)}
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "$1"
+  [ $0
+  ]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/undefined b/users/aspen/emacs/snippets/haskell-mode/undefined
new file mode 100644
index 0000000000..7bcd99b571
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/undefined
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: undefined
+# key: u
+# expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
+# --
+undefined$1
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/action-type b/users/aspen/emacs/snippets/js2-mode/action-type
new file mode 100644
index 0000000000..ef8d1a3863
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/action-type
@@ -0,0 +1,4 @@
+# key: at
+# name: action-type
+# --
+export const ${1:FOO_BAR$(->> yas-text s-upcase (s-replace-all '(("-" . "_") (" " . "_"))))}: '${3:ns}/${1:$(-> yas-text s-dashed-words)}' = '$3/${1:$(-> yas-text s-dashed-words)}'$5
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/before b/users/aspen/emacs/snippets/js2-mode/before
new file mode 100644
index 0000000000..4569b65831
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/before
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: before
+# key: bef
+# --
+before(function() {
+                  $1
+})
diff --git a/users/aspen/emacs/snippets/js2-mode/context b/users/aspen/emacs/snippets/js2-mode/context
new file mode 100644
index 0000000000..d83809f3c3
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/context
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: context
+# key: context
+# --
+context('$1', function() {
+              $2
+})
diff --git a/users/aspen/emacs/snippets/js2-mode/describe b/users/aspen/emacs/snippets/js2-mode/describe
new file mode 100644
index 0000000000..bd0198181d
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/describe
@@ -0,0 +1,6 @@
+# key: desc
+# name: describe
+# --
+describe('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/expect b/users/aspen/emacs/snippets/js2-mode/expect
new file mode 100644
index 0000000000..eba41ef330
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/expect
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: expect
+# key: ex
+# --
+expect($1).$2
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/function b/users/aspen/emacs/snippets/js2-mode/function
new file mode 100644
index 0000000000..b423044b44
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/function
@@ -0,0 +1,6 @@
+# key: f
+# name: function
+# --
+function $1($2) {
+         $3
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/header b/users/aspen/emacs/snippets/js2-mode/header
new file mode 100644
index 0000000000..3e303764cb
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/header
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: header
+# key: hh
+# expand-env: ((yas-indent-line 'fixed))
+# --
+////////////////////////////////////////////////////////////////////////////////
diff --git a/users/aspen/emacs/snippets/js2-mode/it b/users/aspen/emacs/snippets/js2-mode/it
new file mode 100644
index 0000000000..a451cfc08a
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/it
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: it
+# key: it
+# --
+it('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/it-pending b/users/aspen/emacs/snippets/js2-mode/it-pending
new file mode 100644
index 0000000000..00da312e10
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/it-pending
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: it-pending
+# key: xi
+# --
+it('$1')$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/module b/users/aspen/emacs/snippets/js2-mode/module
new file mode 100644
index 0000000000..dc79819d89
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/module
@@ -0,0 +1,12 @@
+# key: module
+# name: module
+# expand-env: ((yas-indent-line (quote fixed)))
+# condition: (= (length "module") (current-column))
+# --
+/**
+ * @fileOverview $1
+ * @name ${2:`(file-name-nondirectory (buffer-file-name))`}
+ * @author Griffin Smith
+ * @license Proprietary
+ */
+$3
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/record b/users/aspen/emacs/snippets/js2-mode/record
new file mode 100644
index 0000000000..0bb0f02436
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/record
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: record
+# key: rec
+# --
+export default class $1 extends Record({
+  $2
+}) {}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/test b/users/aspen/emacs/snippets/js2-mode/test
new file mode 100644
index 0000000000..938d490a74
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/test
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: test
+# key: test
+# --
+test('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub b/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub
new file mode 100644
index 0000000000..d2447e4b5a
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub
@@ -0,0 +1,12 @@
+# -*- mode: snippet -*-
+# name: fetchFromGitHub
+# uuid: fetchFromGitHub
+# key: fetchFromGitHub
+# condition: t
+# --
+fetchFromGitHub {
+                owner = "$1";
+                repo = "$2";
+                rev = "$3";
+                sha256 = "0000000000000000000000000000000000000000000000000000";
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/pythonPackage b/users/aspen/emacs/snippets/nix-mode/pythonPackage
new file mode 100644
index 0000000000..0a74c21e18
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/pythonPackage
@@ -0,0 +1,16 @@
+# key: pypkg
+# name: pythonPackage
+# condition: t
+# --
+${1:pname} = buildPythonPackage rec {
+           name = "\${pname}-\${version}";
+           pname = "$1";
+           version = "${2:1.0.0}";
+           src = fetchPypi {
+               inherit pname version;
+               sha256 = "0000000000000000000000000000000000000000000000000000";
+           };
+           propagatedBuildInputs = with pythonSelf; [
+               $3
+           ];
+};
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/sha256 b/users/aspen/emacs/snippets/nix-mode/sha256
new file mode 100644
index 0000000000..bc640e5ab0
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/sha256
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: sha256
+# uuid: sha256
+# key: sha256
+# condition: t
+# --
+sha256 = "0000000000000000000000000000000000000000000000000000";
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/SQL source block b/users/aspen/emacs/snippets/org-mode/SQL source block
new file mode 100644
index 0000000000..b5d43fd6bc
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/SQL source block
@@ -0,0 +1,6 @@
+# key: sql
+# name: SQL source block
+# --
+#+BEGIN_SRC sql ${1::async}
+$2
+#+END_SRC
diff --git a/users/aspen/emacs/snippets/org-mode/combat b/users/aspen/emacs/snippets/org-mode/combat
new file mode 100644
index 0000000000..b4db0f433a
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/combat
@@ -0,0 +1,13 @@
+# -*- mode: snippet -*-
+# name: combat
+# uuid: combat
+# key: combat
+# condition: t
+# --
+|             | initiative | max hp | current hp | status |      |
+|-------------+------------+--------+------------+--------+------|
+| Barty Barty |            |        |            |        | <--- |
+| Hectoroth   |            |        |            |        |      |
+| Xanadu      |            |        |            |        |      |
+| Aurora      |            |        |            |        |      |
+| EFB         |            |        |            |        |      |
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/date b/users/aspen/emacs/snippets/org-mode/date
new file mode 100644
index 0000000000..297529cdac
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/date
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# key: date
+# name: date.org
+# --
+[`(format-time-string "%Y-%m-%d")`]$0
diff --git a/users/aspen/emacs/snippets/org-mode/date-time b/users/aspen/emacs/snippets/org-mode/date-time
new file mode 100644
index 0000000000..fde469276c
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/date-time
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: date-time
+# key: dt
+# --
+[`(format-time-string "%Y-%m-%d %H:%m:%S")`]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/description b/users/aspen/emacs/snippets/org-mode/description
new file mode 100644
index 0000000000..a43bc95cc3
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/description
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: description
+# key: desc
+# --
+:DESCRIPTION:
+$1
+:END:
diff --git a/users/aspen/emacs/snippets/org-mode/nologdone b/users/aspen/emacs/snippets/org-mode/nologdone
new file mode 100644
index 0000000000..e5be85d6b3
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/nologdone
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: nologdone
+# key: nologdone
+# --
+#+STARTUP: nologdone$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/python source block b/users/aspen/emacs/snippets/org-mode/python source block
new file mode 100644
index 0000000000..247ae51b0b
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/python source block
@@ -0,0 +1,6 @@
+# key: py
+# name: Python source block
+# --
+#+BEGIN_SRC python
+$0
+#+END_SRC
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/reveal b/users/aspen/emacs/snippets/org-mode/reveal
new file mode 100644
index 0000000000..1bdbdfa5dc
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/reveal
@@ -0,0 +1,6 @@
+# key: reveal
+# name: reveal
+# condition: t
+# --
+#+ATTR_REVEAL: :frag ${1:roll-in}
+$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/transaction b/users/aspen/emacs/snippets/org-mode/transaction
new file mode 100644
index 0000000000..37f2dd31ca
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/transaction
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: transaction
+# key: begin
+# --
+BEGIN;
+$0
+ROLLBACK;
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/prolog-mode/tests b/users/aspen/emacs/snippets/prolog-mode/tests
new file mode 100644
index 0000000000..a9d92a0d5b
--- /dev/null
+++ b/users/aspen/emacs/snippets/prolog-mode/tests
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: tests
+# uuid: tests
+# key: tests
+# condition: t
+# --
+:- begin_tests(${1:name}).
+
+$0
+
+:- end_tests($1).
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/prolog-mode/use-module b/users/aspen/emacs/snippets/prolog-mode/use-module
new file mode 100644
index 0000000000..75fd19b641
--- /dev/null
+++ b/users/aspen/emacs/snippets/prolog-mode/use-module
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: use-module
+# uuid: use-module
+# key: use
+# condition: t
+# --
+:- use_module(${1:library($2)}${3:, [$4]}).
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/add_column b/users/aspen/emacs/snippets/python-mode/add_column
new file mode 100644
index 0000000000..47e83850d5
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/add_column
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: add_column
+# key: op.add_column
+# --
+op.add_column('${1:table}', sa.Column('${2:name}', sa.${3:String()}))$0
diff --git a/users/aspen/emacs/snippets/python-mode/decorate b/users/aspen/emacs/snippets/python-mode/decorate
new file mode 100644
index 0000000000..4f96748572
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/decorate
@@ -0,0 +1,15 @@
+# -*- mode: snippet -*-
+# name: decorate
+# uuid: decorate
+# key: decorate
+# condition: t
+# --
+def wrap(inner):
+    @wraps(inner)
+    def wrapped(*args, **kwargs):
+        ret = inner(*args, **kwargs)
+        return ret
+
+    return wrapped
+
+return wrap
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/dunder b/users/aspen/emacs/snippets/python-mode/dunder
new file mode 100644
index 0000000000..71d99dddc6
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/dunder
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: dunder
+# uuid: dunder
+# key: du
+# condition: t
+# --
+__$1__$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/name b/users/aspen/emacs/snippets/python-mode/name
new file mode 100644
index 0000000000..1495cc91d9
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/name
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: name
+# uuid: name
+# key: name
+# condition: t
+# --
+__name__
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/op.get_bind.execute b/users/aspen/emacs/snippets/python-mode/op.get_bind.execute
new file mode 100644
index 0000000000..aba801c6ba
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/op.get_bind.execute
@@ -0,0 +1,7 @@
+# key: exec
+# name: op.get_bind.execute
+# --
+op.get_bind().execute(
+    """
+    `(progn (sqlup-mode) "")`$1
+    """)
diff --git a/users/aspen/emacs/snippets/python-mode/pdb b/users/aspen/emacs/snippets/python-mode/pdb
new file mode 100644
index 0000000000..41c6f87cbf
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/pdb
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: pdb
+# uuid: pdb
+# key: pdb
+# condition: t
+# --
+import pdb; pdb.set_trace()
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/#[macro_use] b/users/aspen/emacs/snippets/rust-mode/#[macro_use]
new file mode 100644
index 0000000000..fea942a337
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/#[macro_use]
@@ -0,0 +1,5 @@
+# key: macro_use
+# name: #[macro_use]
+# --
+#[macro_use]
+${1:extern crate} ${2:something};$0
diff --git a/users/aspen/emacs/snippets/rust-mode/async test b/users/aspen/emacs/snippets/rust-mode/async test
new file mode 100644
index 0000000000..2352d7b56b
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/async test
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: async test
+# uuid: atest
+# key: atest
+# condition: t
+# --
+#[tokio::test${1:(flavor = "multi_thread")}]
+async fn ${2:test_name}() {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/benchmark b/users/aspen/emacs/snippets/rust-mode/benchmark
new file mode 100644
index 0000000000..9ec4307538
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/benchmark
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: benchmark
+# uuid: benchmark
+# key: bench
+# condition: t
+# --
+#[bench]
+fn ${1:benchmark_name}(b: &mut Bencher) {
+   `%`b.iter(|| $0);
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/proptest b/users/aspen/emacs/snippets/rust-mode/proptest
new file mode 100644
index 0000000000..be12af4911
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/proptest
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: proptest
+# uuid: proptest
+# key: proptest
+# condition: t
+# --
+#[proptest]
+fn ${1:test_name}($2) {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/test-module b/users/aspen/emacs/snippets/rust-mode/test-module
new file mode 100644
index 0000000000..bfa2ca2d18
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/test-module
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+mod $1 {
+    use super::*;
+
+    $0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/tests b/users/aspen/emacs/snippets/rust-mode/tests
new file mode 100644
index 0000000000..0a476ab586
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/tests
@@ -0,0 +1,9 @@
+# key: tests
+# name: test module
+# --
+#[cfg(test)]
+mod ${1:tests} {
+    use super::*;
+
+    $0
+}
diff --git a/users/aspen/emacs/snippets/snippet-mode/indent b/users/aspen/emacs/snippets/snippet-mode/indent
new file mode 100644
index 0000000000..d38ffceafb
--- /dev/null
+++ b/users/aspen/emacs/snippets/snippet-mode/indent
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: indent
+# key: indent
+# --
+# expand-env: ((yas-indent-line 'fixed))
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/sql-mode/count(*) group by b/users/aspen/emacs/snippets/sql-mode/count(*) group by
new file mode 100644
index 0000000000..6acc46ff39
--- /dev/null
+++ b/users/aspen/emacs/snippets/sql-mode/count(*) group by
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: count(*) group by
+# key: countby
+# --
+SELECT count(*), ${1:column} FROM ${2:table} GROUP BY $1;
diff --git a/users/aspen/emacs/snippets/terraform-mode/variable b/users/aspen/emacs/snippets/terraform-mode/variable
new file mode 100644
index 0000000000..14822f1a05
--- /dev/null
+++ b/users/aspen/emacs/snippets/terraform-mode/variable
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: variable
+# uuid: variable
+# key: var
+# condition: t
+# --
+variable "${1:name}" {
+  type = ${2:string}
+  ${3:default = ${4:default}}
+}
+$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/text-mode/date b/users/aspen/emacs/snippets/text-mode/date
new file mode 100644
index 0000000000..7b94311470
--- /dev/null
+++ b/users/aspen/emacs/snippets/text-mode/date
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# name: date
+# key: date
+# --
+`(format-time-string "%Y-%m-%d")`$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/expect-test b/users/aspen/emacs/snippets/tuareg-mode/expect-test
new file mode 100644
index 0000000000..e0b541fce4
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/expect-test
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: expect-test
+# uuid: expect-test
+# key: exp
+# condition: t
+# --
+let%expect_test "${1:name}" =
+        ${2:<body>};
+        [%expect {| $3 |}]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/module b/users/aspen/emacs/snippets/tuareg-mode/module
new file mode 100644
index 0000000000..9b1701e3a2
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/module
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: module
+# uuid: module
+# key: mod
+# condition: t
+# --
+module ${1:Name} = struct
+       $0
+end
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/test-module b/users/aspen/emacs/snippets/tuareg-mode/test-module
new file mode 100644
index 0000000000..b16176e5f3
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/test-module
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+let%test_module ${1:_} =
+  (module struct
+    $0
+  end)
\ No newline at end of file