about summary refs log tree commit diff
path: root/configs/shared/.emacs.d/wpc
diff options
context:
space:
mode:
authorWilliam Carroll <wpcarro@gmail.com>2019-10-09T11·13+0100
committerWilliam Carroll <wpcarro@gmail.com>2019-12-24T15·21+0000
commit6b456c1b7a4f6899f063a6e65355af51901d9c7a (patch)
treecfc70d74818ae9fabdbbfb0cf16cce092e4c1a09 /configs/shared/.emacs.d/wpc
parenta7c72adb2ebec1e497fc040eaf3551d564d61a5b (diff)
Massive configuration overhaul
Currently paying the price of months of non-diligent git usage.

Here's what has changed.

- Theming support in Gvcci and wpgtk
- Dropping support for i3
- Supporting EXWM
- Many Elisp modules
- Collapsed redundant directories in ./configs
Diffstat (limited to 'configs/shared/.emacs.d/wpc')
-rw-r--r--configs/shared/.emacs.d/wpc/alist.el227
-rw-r--r--configs/shared/.emacs.d/wpc/bag.el66
-rw-r--r--configs/shared/.emacs.d/wpc/bills.el26
-rw-r--r--configs/shared/.emacs.d/wpc/bookmark.el108
-rw-r--r--configs/shared/.emacs.d/wpc/buffer.el51
-rw-r--r--configs/shared/.emacs.d/wpc/bytes.el109
-rw-r--r--configs/shared/.emacs.d/wpc/cache.el80
-rw-r--r--configs/shared/.emacs.d/wpc/chrome.el78
-rw-r--r--configs/shared/.emacs.d/wpc/clipboard.el74
-rw-r--r--configs/shared/.emacs.d/wpc/colorscheme.el91
-rw-r--r--configs/shared/.emacs.d/wpc/constants.el28
-rw-r--r--configs/shared/.emacs.d/wpc/cycle.el105
-rw-r--r--configs/shared/.emacs.d/wpc/device.el38
-rw-r--r--configs/shared/.emacs.d/wpc/display.el47
-rw-r--r--configs/shared/.emacs.d/wpc/do.el54
-rw-r--r--configs/shared/.emacs.d/wpc/dotfiles.el45
-rw-r--r--configs/shared/.emacs.d/wpc/dotted.el49
-rw-r--r--configs/shared/.emacs.d/wpc/entr.el115
-rw-r--r--configs/shared/.emacs.d/wpc/enum.el98
-rw-r--r--configs/shared/.emacs.d/wpc/fonts.el148
-rw-r--r--configs/shared/.emacs.d/wpc/fs.el59
-rw-r--r--configs/shared/.emacs.d/wpc/functions.el133
-rw-r--r--configs/shared/.emacs.d/wpc/google-stuff.el183
-rw-r--r--configs/shared/.emacs.d/wpc/google-tooling.el53
-rw-r--r--configs/shared/.emacs.d/wpc/graph.el91
-rw-r--r--configs/shared/.emacs.d/wpc/imdb.el128
-rw-r--r--configs/shared/.emacs.d/wpc/irc.el79
-rw-r--r--configs/shared/.emacs.d/wpc/iso.el95
-rw-r--r--configs/shared/.emacs.d/wpc/ivy-helpers.el31
-rw-r--r--configs/shared/.emacs.d/wpc/kaomoji.el45
-rw-r--r--configs/shared/.emacs.d/wpc/kbd.el90
-rw-r--r--configs/shared/.emacs.d/wpc/keyboard.el147
-rw-r--r--configs/shared/.emacs.d/wpc/keymap.el25
-rw-r--r--configs/shared/.emacs.d/wpc/laptop-battery.el60
-rw-r--r--configs/shared/.emacs.d/wpc/list.el197
-rw-r--r--configs/shared/.emacs.d/wpc/list.nix8
-rw-r--r--configs/shared/.emacs.d/wpc/macros.el95
-rw-r--r--configs/shared/.emacs.d/wpc/math.el59
-rw-r--r--configs/shared/.emacs.d/wpc/maybe.el102
-rw-r--r--configs/shared/.emacs.d/wpc/me-seconds.el245
-rw-r--r--configs/shared/.emacs.d/wpc/monoid.el30
-rw-r--r--configs/shared/.emacs.d/wpc/number.el151
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-clojure.el85
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-company.el28
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-dired.el39
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-docker.el16
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-elixir.el13
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-flycheck.el14
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-haskell.el56
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-java.el42
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-javascript.el61
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-keybindings.el206
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-lisp.el114
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-misc.el209
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-nix.el12
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-ocaml.el51
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-org.el78
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-package.el27
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-python.el17
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-reasonml.el29
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-rust.el34
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-shell.el15
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-terminal.el70
-rw-r--r--configs/shared/.emacs.d/wpc/packages/wpc-ui.el185
-rw-r--r--configs/shared/.emacs.d/wpc/playback.el25
-rw-r--r--configs/shared/.emacs.d/wpc/polymorphism.el37
-rw-r--r--configs/shared/.emacs.d/wpc/prelude.el122
-rw-r--r--configs/shared/.emacs.d/wpc/prelude.nix11
-rw-r--r--configs/shared/.emacs.d/wpc/productivity-timer.el22
-rw-r--r--configs/shared/.emacs.d/wpc/pulse-audio.el48
-rw-r--r--configs/shared/.emacs.d/wpc/pushover.el75
-rw-r--r--configs/shared/.emacs.d/wpc/random.el73
-rw-r--r--configs/shared/.emacs.d/wpc/scheduler.el22
-rw-r--r--configs/shared/.emacs.d/wpc/scope.el99
-rw-r--r--configs/shared/.emacs.d/wpc/screen-brightness.el57
-rw-r--r--configs/shared/.emacs.d/wpc/sequence.el105
-rw-r--r--configs/shared/.emacs.d/wpc/series.el81
-rw-r--r--configs/shared/.emacs.d/wpc/set.el92
-rw-r--r--configs/shared/.emacs.d/wpc/sre.el26
-rw-r--r--configs/shared/.emacs.d/wpc/ssh.el31
-rw-r--r--configs/shared/.emacs.d/wpc/stack.el93
-rw-r--r--configs/shared/.emacs.d/wpc/string.el126
-rw-r--r--configs/shared/.emacs.d/wpc/string.nix8
-rw-r--r--configs/shared/.emacs.d/wpc/struct.el71
-rw-r--r--configs/shared/.emacs.d/wpc/symbol.el43
-rw-r--r--configs/shared/.emacs.d/wpc/themes.el170
-rw-r--r--configs/shared/.emacs.d/wpc/todo.el297
-rw-r--r--configs/shared/.emacs.d/wpc/tree.el193
-rw-r--r--configs/shared/.emacs.d/wpc/tuple.el86
-rw-r--r--configs/shared/.emacs.d/wpc/vector.el59
-rw-r--r--configs/shared/.emacs.d/wpc/wallpaper.el84
-rw-r--r--configs/shared/.emacs.d/wpc/window-manager.el672
-rw-r--r--configs/shared/.emacs.d/wpc/window.el37
-rw-r--r--configs/shared/.emacs.d/wpc/wpgtk.el45
-rw-r--r--configs/shared/.emacs.d/wpc/zle.el90
95 files changed, 8144 insertions, 0 deletions
diff --git a/configs/shared/.emacs.d/wpc/alist.el b/configs/shared/.emacs.d/wpc/alist.el
new file mode 100644
index 0000000000..da0ce68b8f
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/alist.el
@@ -0,0 +1,227 @@
+;;; alist.el --- Interface for working with associative lists -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Firstly, a rant:
+;; In most cases, I find Elisp's APIs to be confusing.  There's a mixture of
+;; overloaded functions that leak the implementation details (TODO: provide an
+;; example of this.) of the abstract data type, which I find privileges those
+;; "insiders" who spend disproportionately large amounts of time in Elisp land,
+;; and other functions with little-to-no pattern about the order in which
+;; arguments should be applied.  In theory, however, most of these APIs could
+;; and should be much simpler.  This module represents a step in that direction.
+;;
+;; I'm modelling these APIs after Elixir's APIs.
+;;
+;; On my wishlist is to create protocols that will allow generic interfaces like
+;; Enum protocols, etc.  Would be nice to abstract over...
+;; - associative lists (i.e. alists)
+;; - property lists (i.e. plists)
+;; - hash tables
+;; ...with some dictionary or map-like interface.  This will probably end up
+;; being quite similar to the kv.el project but with differences at the API
+;; layer.
+;;
+;; Similar libraries:
+;; - map.el: Comes bundled with recent versions of Emacs.
+;; - asoc.el: Helpers for working with alists.  asoc.el is similar to alist.el
+;;   because it uses the "!" convention for signalling that a function mutates
+;;   the underlying data structure.
+;; - ht.el: Hash table library.
+;; - kv.el: Library for dealing with key-value collections.  Note that map.el
+;;   has a similar typeclass because it works with lists, hash-tables, or
+;;   arrays.
+;; - a.el: Clojure-inspired way of working with key-value data structures in
+;; Elisp.  Works with alists, hash-tables, and sometimes vectors.
+;;
+;; Some API design principles:
+;; - The "noun" (i.e. alist) of the "verb" (i.e. function) comes last to improve
+;; composability with the threading macro (i.e. `->>') and to improve consumers'
+;; intuition with the APIs.  Learn this once, know it always.
+;;
+;; - Every function avoids mutating the alist unless it ends with !.
+;;
+;; - CRUD operations will be named according to the following table:
+;;   - "create" *and* "set"
+;;   - "read"   *and* "get"
+;;   - "update"
+;;   - "delete" *and* "remove"
+;;
+;; For better or worse, all of this code expects alists in the form of:
+;; ((first-name . "William") (last-name . "Carroll"))
+;;
+;; Special thanks to github.com/alphapapa/emacs-package-dev-handbook for some of
+;; the idiomatic ways to update alists.
+;;
+;; TODO: Include a section that compares alist.el to a.el from
+;; github.com/plexus/a.el.
+
+;; Dependencies:
+
+;; TODO: Consider dropping explicit dependency white-listing since all of these
+;; should be available in my Emacs.  The problem arises when this library needs
+;; to be published, in which case, something like Nix and a build process could
+;; possible insert the necessary require statements herein.  Not sure how I feel
+;; about this though.
+(require 'maybe)
+(require 'macros)
+(require 'dash)
+(require 'tuple)
+(require 'maybe)
+
+;;; Code:
+
+;; TODO: Support function aliases for:
+;; - create/set
+;; - read/get
+;; - update
+;; - delete/remove
+
+;; Support mutative variants of functions with an ! appendage to their name.
+
+;; Ensure that the same message about only updating the first occurrence of a
+;; key is consistent throughout documentation using string interpolation or some
+;; other mechanism.
+
+;; TODO: Consider wrapping all of this with `(cl-defstruct alist xs)'.
+
+(defun alist/new ()
+  "Return a new, empty alist."
+  '())
+
+;; Create
+;; TODO: See if this mutates.
+(defun alist/set (k v xs)
+  "Set K to V in XS."
+  (if (alist/has-key? k xs)
+      (progn
+        (setf (alist-get k xs) v)
+        xs)
+    (list/cons `(,k . ,v) xs)))
+
+(defun alist/set! (k v xs)
+  "Set K to V in XS mutatively.
+Note that this doesn't append to the alist in the way that most alists handle
+  writing.  If the k already exists in XS, it is overwritten."
+  (map-delete xs k)
+  (map-put xs k v))
+
+;; Read
+(defun alist/get (k xs)
+  "Return the value at K in XS; otherwise, return nil.
+Returns the first occurrence of K in XS since alists support multiple entries."
+  (cdr (assoc k xs)))
+
+(defun alist/get-entry (k xs)
+  "Return the first key-value pair at K in XS."
+  (assoc k xs))
+
+;; Update
+;; TODO: Add warning about only the first occurrence being updated in the
+;; documentation.
+(defun alist/update (k f xs)
+  "Apply F to the value stored at K in XS.
+If `K' is not in `XS', this function errors.  Use `alist/upsert' if you're
+interested in inserting a value when a key doesn't already exist."
+  (if (maybe/nil? (alist/get k xs))
+      (error "Refusing to update: key does not exist in alist")
+    (alist/set k (funcall f (alist/get k xs)) xs)))
+
+(defun alist/update! (k f xs)
+  "Call F on the entry at K in XS.
+Mutative variant of `alist/update'."
+  (alist/set! k (funcall f (alist/get k xs))xs))
+
+;; TODO: Support this.
+(defun alist/upsert (k v f xs)
+  "If K exists in `XS' call `F' on the value otherwise insert `V'."
+  (if (alist/get k xs)
+      (alist/update k f xs)
+    (alist/set k v xs)))
+
+;; Delete
+;; TODO: Make sure `delete' and `remove' behave as advertised in the Elisp docs.
+(defun alist/delete (k xs)
+  "Deletes the entry of K from XS.
+This only removes the first occurrence of K, since alists support multiple
+  key-value entries.  See `alist/delete-all' and `alist/dedupe'."
+  (remove (assoc k xs) xs))
+
+(defun alist/delete! (k xs)
+  "Delete the entry of K from XS.
+Mutative variant of `alist/delete'."
+  (delete (assoc k xs) xs))
+
+;; Additions to the CRUD API
+;; TODO: Implement this function.
+(defun alist/dedupe-keys (xs)
+  "Remove the entries in XS where the keys are `equal'.")
+
+(defun alist/dedupe-entries (xs)
+  "Remove the entries in XS where the key-value pair are `equal'."
+  (delete-dups xs))
+
+(defun alist/keys (xs)
+  "Return a list of the keys in XS."
+  (mapcar 'car xs))
+
+(defun alist/values (xs)
+  "Return a list of the values in XS."
+  (mapcar 'cdr xs))
+
+(defun alist/has-key? (k xs)
+  "Return t if XS has a key `equal' to K."
+  (maybe/some? (assoc k xs)))
+
+(defun alist/has-value? (v xs)
+  "Return t if XS has a value of V."
+  (maybe/some? (rassoc v xs)))
+
+(defun alist/count (xs)
+  "Return the number of entries in XS."
+  (length xs))
+
+(defun alist/reduce (acc f xs)
+  "Return a new alist by calling, F, on k v and ACC from XS.
+F should return a tuple.  See tuple.el for more information."
+  (->> (alist/keys xs)
+       (list/reduce acc
+                    (lambda (k acc)
+                      (funcall f k (alist/get k xs) acc)))))
+
+(defun alist/merge (a b)
+  "Return a new alist with a merge of alists, A and B.
+In this case, the last writer wins, which is B."
+  (alist/reduce a #'alist/set b))
+
+;; TODO: Support `-all' variants like:
+;; - get-all
+;; - delete-all
+;; - update-all
+
+;; Scratch-pad
+(macros/comment
+ (progn
+   (setq person '((first-name . "William")
+                  (first-name . "William")
+                  (last-name  . "Carroll")
+                  (last-name  . "Another")))
+   (alist/set 'last-name "Van Gogh" person)
+   (alist/get 'last-name person)
+   (alist/update 'last-name (lambda (x) "whoops") person)
+   (alist/delete 'first-name person)
+   (alist/keys person)
+   (alist/values person)
+   (alist/count person)
+   (alist/has-key? 'first-name person)
+   (alist/has-value? "William" person)
+   ;; (alist/dedupe-keys person)
+   (alist/dedupe-entries person)
+   (alist/count person)))
+
+;; Tests
+
+;; TODO: Support test cases for the entire API.
+
+(provide 'alist)
+;;; alist.el ends here
diff --git a/configs/shared/.emacs.d/wpc/bag.el b/configs/shared/.emacs.d/wpc/bag.el
new file mode 100644
index 0000000000..c9511b18e7
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/bag.el
@@ -0,0 +1,66 @@
+;;; bag.el --- Working with bags (aka multi-sets) -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; What is a bag?  A bag should be thought of as a frequency table.  It's a way
+;; to convert a list of something into a set that allows duplicates.  Isn't
+;; allowing duplicates the whole thing with Sets?  Kind of.  But the interface
+;; of Sets is something that bags resemble, so multi-set isn't as bag of a name
+;; as it may first seem.
+;;
+;; If you've used Python's collections.Counter, the concept of a bag should be
+;; familiar already.
+;;
+;; Interface:
+;; - add        :: x -> Bag(x) -> Bag(x)
+;; - remove     :: x -> Bag(x) -> Bag(x)
+;; - union      :: Bag(x) -> Bag(x) -> Bag(x)
+;; - difference :: Bag(x) -> Bag(x) -> Bag(x)
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'number)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct bag xs)
+
+(defun bag/update (f xs)
+  "Call F on alist in XS."
+  (let ((ys (bag-xs xs)))
+    (setf (bag-xs xs) (funcall f ys))))
+
+(defun bag/new ()
+  "Create an empty bag."
+  (make-bag :xs (alist/new)))
+
+(defun bag/contains? (x xs)
+  "Return t if XS has X."
+  (alist/has-key? x (bag-xs xs)))
+
+;; TODO: Tabling this for now since working with structs seems to be
+;; disappointingly difficult.  Where is `struct/update'?
+;; (defun bag/add (x xs)
+;;   "Add X to XS.")
+
+;; TODO: What do we name delete vs. remove?
+;; (defun bag/remove (x xs)
+;;   "Remove X from XS.
+;; This is a no-op is X doesn't exist in XS.")
+
+(defun bag/from-list (xs)
+  "Map a list of `XS' into a bag."
+  (->> xs
+       (list/reduce
+        (bag/new)
+        (lambda (x acc)
+          (bag/add x 1 #'number/inc acc)))))
+
+(provide 'bag)
+;;; bag.el ends here
diff --git a/configs/shared/.emacs.d/wpc/bills.el b/configs/shared/.emacs.d/wpc/bills.el
new file mode 100644
index 0000000000..fbdeb9d0f8
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/bills.el
@@ -0,0 +1,26 @@
+;;; bills.el --- Helping me manage my bills -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; For personal use only.
+
+;;; Code:
+
+(defconst bills/whitelist '(("Council Tax" . "rbkc.gov.uk/onlinepayments/counciltaxpayments/")
+                            ("Internet". "plus.net/member-centre/login"))
+  "Maps searchable labels to URLs to pay these bills.")
+
+(defun bills/url ()
+  "Copies the URL to pay a bill onto the clipboard."
+  (ivy-read
+   "Bill: "
+   bills/whitelist
+   :action (lambda (entry)
+             (kill-new (cdr entry))
+             (alert "Copied to clipboard!"))))
+
+(macros/comment
+ (bills/url))
+
+(provide 'bills)
+;;; bills.el ends here
diff --git a/configs/shared/.emacs.d/wpc/bookmark.el b/configs/shared/.emacs.d/wpc/bookmark.el
new file mode 100644
index 0000000000..2a4156041e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/bookmark.el
@@ -0,0 +1,108 @@
+;;; bookmark.el --- Saved files and directories on my filesystem -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; After enjoying and relying on Emacs's builtin `jump-to-register' command, I'd
+;; like to recreate this functionality with a few extensions.
+;;
+;; Everything herein will mimmick my previous KBDs for `jump-to-register', which
+;; were <leader>-j-<register-kbd>.  If the `bookmark-path' is a file, Emacs will
+;; open a buffer with that file.  If the `bookmark-path' is a directory, Emacs
+;; will open an ivy window searching that directory.
+
+;;; Code:
+
+(require 'f)
+(require 'buffer)
+(require 'list)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct bookmark label path kbd)
+
+;; TODO: Consider hosting this function somewhere other than here, since it
+;; feels useful above of the context of bookmarks.
+;; TODO: Assess whether it'd be better to use the existing function:
+;; `counsel-projectile-switch-project-action'.  See the noise I made on GH for
+;; more context: https://github.com/ericdanan/counsel-projectile/issues/137
+(defun bookmark/handle-directory-dwim (path)
+  "Open PATH as either a project directory or a regular directory.
+If PATH is `projectile-project-p', open with `counsel-projectile-find-file'.
+Otherwise, open with `counsel-find-file'."
+  (if (projectile-project-p path)
+      (with-temp-buffer
+        (cd (projectile-project-p path))
+        (call-interactively #'counsel-projectile-find-file))
+    (let ((ivy-extra-directories nil))
+      (counsel-find-file path))))
+
+(defconst bookmark/handle-directory #'bookmark/handle-directory-dwim
+  "Function to call when a bookmark points to a directory.")
+
+(defconst bookmark/handle-file #'counsel-find-file-action
+  "Function to call when a bookmark points to a file.")
+
+(defconst bookmark/whitelist
+  (list
+   ;; TODO: Consider using (getenv "ORG_DIRECTORY")
+   (make-bookmark :label "org"
+                  :path "~/Dropbox/org"
+                  :kbd "o")
+   (make-bookmark :label "dotfiles"
+                  :path "~/Dropbox/dotfiles"
+                  :kbd "d")
+   (make-bookmark :label "current project"
+                  :path constants/current-project
+                  :kbd "p"))
+  "List of registered bookmarks.")
+
+(defconst bookmark/install-kbds? t
+  "When t, install keybindings.")
+
+;; TODO: Consider `ivy-read' extension that takes a list of structs,
+;; `struct-to-label' and `label-struct' functions.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; API
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bookmark/open (b)
+  "Open bookmark, B, in a new buffer or an ivy minibuffer."
+  (let ((path (bookmark-path b)))
+    (cond
+     ((f-directory? path)
+      (funcall bookmark/handle-directory path))
+     ((f-file? path)
+      (funcall bookmark/handle-file path)))))
+
+(defun bookmark/ivy-open ()
+  "Use ivy to filter available bookmarks."
+  (interactive)
+  (ivy-read "Bookmark: "
+            (->> bookmark/whitelist
+                 (list/map #'bookmark-label))
+            :require-match t
+            :action (lambda (label)
+                      (->> bookmark/whitelist
+                           (list/find
+                            (lambda (b)
+                              (equal label (bookmark-label b))))
+                           bookmark/open))))
+
+(when bookmark/install-kbds?
+  (evil-leader/set-key
+    "jj" #'bookmark/ivy-open)
+  (->> bookmark/whitelist
+       (list/map
+        (lambda (b)
+          (evil-leader/set-key
+            (string/concat "j" (bookmark-kbd b))
+            ;; TODO: Consider `cl-labels' so `which-key' minibuffer is more
+            ;; helpful.
+            (lambda () (interactive) (bookmark/open b)))))))
+
+(provide 'bookmark)
+;;; bookmark.el ends here
diff --git a/configs/shared/.emacs.d/wpc/buffer.el b/configs/shared/.emacs.d/wpc/buffer.el
new file mode 100644
index 0000000000..9de14b4bd4
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/buffer.el
@@ -0,0 +1,51 @@
+;;; buffer.el --- Working with Emacs buffers -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Utilities for CRUDing buffers in Emacs.
+;;
+;; Many of these functions may seem unnecessary especially when you consider
+;; there implementations.  In general I believe that Elisp suffers from a
+;; library disorganization problem.  Providing simple wrapper functions that
+;; rename functions or reorder parameters is worth the effort in my opinion if
+;; it improves discoverability (via intuition) and improve composability.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun buffer/exists? (name)
+  "Return t if buffer, NAME, exists."
+  (maybe/some? (buffer/find name)))
+
+(defun buffer/find (buffer-or-name)
+  "Find a buffer by its BUFFER-OR-NAME."
+  (get-buffer buffer-or-name))
+
+(defun buffer/new (name)
+  "Return a newly created buffer NAME."
+  (generate-new-buffer name))
+
+(defun buffer/find-or-create (name)
+  "Find or create buffer, NAME.
+Return a reference to that buffer."
+  (let ((x (buffer/find name)))
+    (if (maybe/some? x)
+        x
+      (buffer/new name))))
+
+;; TODO: Should this consume: `display-buffer' or `switch-to-buffer'?
+(defun buffer/show (buffer-or-name)
+  "Display the BUFFER-OR-NAME, which is either a buffer reference or its name."
+  (display-buffer buffer-or-name))
+
+(provide 'buffer)
+;;; buffer.el ends here
diff --git a/configs/shared/.emacs.d/wpc/bytes.el b/configs/shared/.emacs.d/wpc/bytes.el
new file mode 100644
index 0000000000..d8bd2e2886
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/bytes.el
@@ -0,0 +1,109 @@
+;;; bytes.el --- Working with byte values -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Functions to help with human-readable representations of byte values.
+;;
+;; Usage:
+;; See the test cases for example usage.  Or better yet, I should use a type of
+;; structured documentation that would allow me to expose a view into the test
+;; suite here.  Is this currently possible in Elisp?
+;;
+;; API:
+;; - serialize :: Integer -> String
+;;
+;; Wish list:
+;; - Rounding: e.g. (bytes (* 1024 1.7)) => "2KB"
+
+;;; Code:
+
+;; TODO: Support -ibabyte variants like Gibibyte (GiB).
+
+;; Ranges:
+;;  B: [   0,  1e3)
+;; KB: [ 1e3,  1e6)
+;; MB: [ 1e6,  1e6)
+;; GB: [ 1e9, 1e12)
+;; TB: [1e12, 1e15)
+;; PB: [1e15, 1e18)
+;;
+;; Note: I'm currently not support exabytes because that causes the integer to
+;;  overflow.  I imagine a larger integer type may exist, but for now, I'll
+;;  treat this as a YAGNI.
+
+(require 'prelude)
+(require 'tuple)
+(require 'math)
+(require 'number)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst bytes/kb (math/exp 2 10)
+  "Number of bytes in a kilobyte.")
+
+(defconst bytes/mb (math/exp 2 20)
+  "Number of bytes in a megabytes.")
+
+(defconst bytes/gb (math/exp 2 30)
+  "Number of bytes in a gigabyte.")
+
+(defconst bytes/tb (math/exp 2 40)
+  "Number of bytes in a terabyte.")
+
+(defconst bytes/pb (math/exp 2 50)
+  "Number of bytes in a petabyte.")
+
+(defconst bytes/eb (math/exp 2 60)
+  "Number of bytes in an exabyte.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bytes/classify (x)
+  "Return unit that closest fits byte count, X."
+  (prelude/assert (number/whole? x))
+  (cond
+   ((and (>= x 0)        (< x bytes/kb))     'byte)
+   ((and (>= x bytes/kb) (< x bytes/mb)) 'kilobyte)
+   ((and (>= x bytes/mb) (< x bytes/gb)) 'megabyte)
+   ((and (>= x bytes/gb) (< x bytes/tb)) 'gigabyte)
+   ((and (>= x bytes/tb) (< x bytes/pb)) 'terabyte)
+   ((and (>= x bytes/pb) (< x bytes/eb)) 'petabyte)))
+
+(defun bytes/to-string (x)
+  "Convert integer X into a human-readable string."
+  (let ((base-and-unit
+         (pcase (bytes/classify x)
+           ('byte     (tuple/from        1 "B"))
+           ('kilobyte (tuple/from bytes/kb "KB"))
+           ('megabyte (tuple/from bytes/mb "MB"))
+           ('gigabyte (tuple/from bytes/gb "GB"))
+           ('terabyte (tuple/from bytes/tb "TB"))
+           ('petabyte (tuple/from bytes/pb "PB")))))
+    (string/format "%d%s"
+                   (round x (tuple/first base-and-unit))
+                   (tuple/second base-and-unit))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(progn
+  (prelude/assert
+   (equal "1000B" (bytes/to-string 1000)))
+  (prelude/assert
+   (equal "2KB" (bytes/to-string (* 2 bytes/kb))))
+  (prelude/assert
+   (equal "17MB" (bytes/to-string (* 17 bytes/mb))))
+  (prelude/assert
+   (equal "419GB" (bytes/to-string (* 419 bytes/gb))))
+  (prelude/assert
+   (equal "999TB" (bytes/to-string (* 999 bytes/tb))))
+  (prelude/assert
+   (equal "2PB" (bytes/to-string (* 2 bytes/pb)))))
+
+(provide 'bytes)
+;;; bytes.el ends here
diff --git a/configs/shared/.emacs.d/wpc/cache.el b/configs/shared/.emacs.d/wpc/cache.el
new file mode 100644
index 0000000000..7b7e1aa2a3
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/cache.el
@@ -0,0 +1,80 @@
+;;; cache.el --- Caching things -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; An immutable cache data structure.
+;;
+;; This is like a sideways stack, that you can pull values out from and re-push
+;; to the top.  It'd be like a stack supporting push, pop, pull.
+;;
+;; This isn't a key-value data-structure like you might expect from a
+;; traditional cache.  The name is subject to change, but the underlying idea of
+;; a cache remains the same.
+;;
+;; Think about prescient.el, which uses essentially an LRU cache integrated into
+;; counsel to help create a "clairovoyant", self-organizing list.
+;;
+;; Use-cases:
+;; - Keeps an cache of workspaces sorted as MRU with an LRU eviction strategy.
+
+;;; Code:
+
+(require 'prelude)
+(require 'struct)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct cache xs)
+
+;; TODO: Prefer another KBD for yasnippet form completion than company-mode's
+;; current KBD.
+
+(defun cache/from-list (xs)
+  "Turn list, XS, into a cache."
+  (make-cache :xs xs))
+
+(defun cache/contains? (x xs)
+  "Return t if X in XS."
+  (->> xs
+       cache-xs
+       (list/contains? x)))
+
+(defun cache/touch (x xs)
+  "Ensure value X in cache, XS, is front of the list.
+If X isn't in XS (using `equal'), insert it at the front."
+  (struct/update
+   cache
+   xs
+   (>> (list/reject (lambda (y) (equal x y)))
+       (list/cons x))
+   xs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(progn
+  (let ((cache (cache/from-list '("chicken" "nugget"))))
+    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+    ;; contains?/2
+    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+    (prelude/refute
+     (cache/contains? "turkey" cache))
+    (prelude/assert
+     (cache/contains? "chicken" cache))
+    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+    ;; touch/2
+    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+    (prelude/assert
+     (equal
+      (cache/touch "nugget" cache)
+      (cache/from-list '("nugget" "chicken"))))
+    (prelude/assert
+     (equal
+      (cache/touch "spicy" cache)
+      (cache/from-list '("spicy" "chicken" "nugget"))))))
+
+(provide 'cache)
+;;; cache.el ends here
diff --git a/configs/shared/.emacs.d/wpc/chrome.el b/configs/shared/.emacs.d/wpc/chrome.el
new file mode 100644
index 0000000000..a27c9dd396
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/chrome.el
@@ -0,0 +1,78 @@
+;;; chrome.el --- Helpers for Google Chrome -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Some helper functions for working with Google Chrome.
+
+;;; Code:
+
+(require 'macros)
+(require 'alist)
+(require 'list)
+
+(defvar chrome/install-kbds? t
+  "If t, install keybinding.")
+
+;; TODO: Consider modelling this as a rose-tree that can nest itself
+;; arbitrarily.
+;; TODO: Consider exporting existing chrome bookmarks.
+(defconst chrome/label->url
+  '(("Google" . "www.google.com")
+    ("Hacker News" . "news.ycombinator.com")
+    ("Gmail" . "www.gmail.com")
+    ("WhatsApp" . "web.whatsapp.com")
+    ("Google Chat" . "chat/")
+    ("Google Calendar" . "calendar/")
+    ("Teknql" . "teknql.slack.com/messages")
+    ("Twitter" . "twitter.com"))
+  "Mapping labels to urls for my bookmarks.")
+
+(defconst chrome/splash-pages
+  '("Google Calendar"
+    "Gmail"
+    "Google Chat"
+    "WhatsApp"
+    "Teknql")
+  "The pages that should open when I open Chrome.")
+
+;; TODO: Add defensive check to start chrome if it isn't already open.
+
+;; TODO: Support option to create new session even if one already exists.
+
+(defun chrome/open-splash-pages ()
+  "Opens Chrome with my preferred splash pages."
+  (interactive)
+  (->> chrome/splash-pages
+       (-map (lambda (x) (alist/get x chrome/label->url)))
+       chrome/open-urls))
+
+;; TODO: Support optional kwargs.
+(cl-defun chrome/open-url (url &keys new-window?)
+  "Opens `URL' in google-chrome.
+Will open without toolbars if APP-MODE? is t."
+  (shell-command (s-concat
+                  "google-chrome "
+                  (if new-window? "--new-window " "")
+                  url)))
+
+(defun chrome/open-urls (urls)
+  "Open multiple `URLS' in chrome."
+  (chrome/open-url
+   (list/join " " urls)))
+
+(defun chrome/browse ()
+  "Display a counsel window for browsing URLs."
+  (interactive)
+  (ivy-read
+   "URL: "
+   chrome/label->url
+   :action (lambda (entry)
+             (chrome/open-url (cdr entry)))))
+
+(when chrome/install-kbds?
+  (evil-leader/set-key
+    "cb" #'chrome/browse
+    "cs" #'chrome/open-splash-pages))
+
+(provide 'chrome)
+;;; chrome.el ends here
diff --git a/configs/shared/.emacs.d/wpc/clipboard.el b/configs/shared/.emacs.d/wpc/clipboard.el
new file mode 100644
index 0000000000..975e06c506
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/clipboard.el
@@ -0,0 +1,74 @@
+;;; clipboard.el --- Working with X11's pasteboard -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Simple functions for copying and pasting.
+;;
+;; Integrate with bburns/clipmon so that System Clipboard can integrate with
+;; Emacs's kill-ring.
+;;
+;; Wish list:
+;; - Create an Emacs integration with github.com/cdown/clipmenud.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bytes)
+
+;; autoinsert feature feels unappealing at first attempt.
+(use-package clipmon
+  :config
+  ;; If this is too large, it could be set machine-dependently, so use
+  ;; `clipboard/print-clipboard-size' to help troubleshoot this if it becomes
+  ;; problematic.
+  (setq kill-ring-max 500)
+  (add-to-list 'after-init-hook #'clipmon-mode-start)
+  (clipmon-mode 1))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar clipboard/install-kbds? t
+  "When t, install keybindings.")
+
+(defun clipboard/copy (x)
+  "Copy string, X, to X11's clipboard."
+  (kill-new x)
+  (message "Copied!"))
+
+(defun clipboard/paste ()
+  "Paste contents of X11 clipboard."
+  (yank)
+  (message "Pasted!"))
+
+(defun clipboard/print-clipboard-size ()
+  "Message the size (in Bytes) of `kill-ring'."
+  (interactive)
+  (->> (clipmon-kill-ring-total)
+       bytes/to-string
+       message))
+
+(defun clipboard/ivy-select ()
+  "Use counsel to copy the selected entry to the system clipboard.
+NOTE: A function, `counsel-yank-pop', exists that does something similar.
+  However instead of copying the entry to the system clipboard, it inserts it
+  where the current point is."
+  (interactive)
+  (ivy-read "kill-ring: " (counsel--yank-pop-kills)
+            :require-match t
+            :action #'clipboard/copy))
+
+;; TODO: Support ivy-actions to insert into an Emacs buffer when an Emacs buffer
+;; was the last active buffer.  However, if an X window is the last buffer,
+;; maybe use xdotool to insert the selected entry.  This would be a bit of a
+;; DWIM command.
+(when clipboard/install-kbds?
+  (exwm-input-set-key
+   (kbd "C-M-v") #'clipboard/ivy-select))
+
+(provide 'clipboard)
+;;; clipboard.el ends here
diff --git a/configs/shared/.emacs.d/wpc/colorscheme.el b/configs/shared/.emacs.d/wpc/colorscheme.el
new file mode 100644
index 0000000000..349d6f8e7e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/colorscheme.el
@@ -0,0 +1,91 @@
+;;; colorscheme.el --- Syntax highlight and friends -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;;
+;; TODO: Clarify this.
+;; Since I have my own definition of "theme", which couples wallpaper, font,
+;; with Emacs's traditional notion of the word "theme", I'm choosing to use
+;; "colorscheme" to refer to *just* the notion of syntax highlight etc.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cycle)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defcustom colorscheme/install-kbds? t
+  "If non-nil, enable the keybindings.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defcustom colorscheme/whitelist
+  (cycle/from-list
+   (custom-available-themes))
+  "The whitelist of colorschemes through which to cycle.")
+
+(defun colorscheme/current ()
+  "Return the currently enabled colorscheme."
+  (cycle/current colorscheme/whitelist))
+
+(defun colorscheme/disable-all ()
+  "Disable all currently enabled colorschemes."
+  (interactive)
+  (->> custom-enabled-themes
+       (list/map #'disable-theme)))
+
+(defun colorscheme/set (theme)
+    "Call `load-theme' with `THEME', ensuring that the line numbers are bright.
+There is no hook that I'm aware of to handle this more elegantly."
+    (load-theme theme)
+    (prelude/set-line-number-color "#da5468"))
+
+(defun colorscheme/whitelist-set (colorscheme)
+  "Focus the COLORSCHEME in the `colorscheme/whitelist' cycle."
+  (cycle/focus (lambda (x) (equal x colorscheme)) colorscheme/whitelist)
+  (colorscheme/set (colorscheme/current)))
+
+(defun colorscheme/ivy-select ()
+  "Load a colorscheme using ivy."
+  (interactive)
+  (let ((theme (ivy-read "Theme: " (cycle/to-list colorscheme/whitelist))))
+    (colorscheme/disable-all)
+    (colorscheme/set (intern theme))))
+
+(cl-defun colorscheme/cycle (&key forward?)
+  "Cycle next if `FORWARD?' is non-nil.
+Cycle prev otherwise."
+  (disable-theme (cycle/current colorscheme/whitelist))
+  (let ((theme (if forward?
+                   (cycle/next colorscheme/whitelist)
+                 (cycle/prev colorscheme/whitelist))))
+    (colorscheme/load theme)
+    (message (s-concat "Active theme: " (symbol/to-string theme)))))
+
+(defun colorscheme/next ()
+  "Disable the currently active theme and load the next theme."
+  (interactive)
+  (colorscheme/cycle :forward? t))
+
+(defun colorscheme/prev ()
+  "Disable the currently active theme and load the previous theme."
+  (interactive)
+  (colorscheme/cycle :forward? nil))
+
+;; Keybindings
+;; TODO: Prefer minor mode definition with a custom keymap.
+(when colorscheme/install-kbds?
+  (evil-leader/set-key
+    "Ft" #'colorscheme/next
+    "Pt" #'colorscheme/prev))
+
+(provide 'colorscheme)
+;;; colorscheme.el ends here
diff --git a/configs/shared/.emacs.d/wpc/constants.el b/configs/shared/.emacs.d/wpc/constants.el
new file mode 100644
index 0000000000..96f5a54cf4
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/constants.el
@@ -0,0 +1,28 @@
+;;; constants.el --- Constants for organizing my Emacs -*- lexical-binding: t -*-
+;; Authpr: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This file contains constants that are shared across my configuration.
+
+;;; Code:
+
+;; TODO: Consider merging `ui.el' and `misc.el' because those are the only
+;; current consumers of these constants, and I'm unsure if the indirection that
+;; globally defined constants introduces is worth it.
+
+(defconst constants/current-project "~/Dropbox/ide/src"
+  "Variable holding the directory for my currently active project.")
+
+(defconst constants/mouse-kbds
+  '([mouse-1] [down-mouse-1] [drag-mouse-1] [double-mouse-1] [triple-mouse-1]
+    [mouse-2] [down-mouse-2] [drag-mouse-2] [double-mouse-2] [triple-mouse-2]
+    [mouse-3] [down-mouse-3] [drag-mouse-3] [double-mouse-3] [triple-mouse-3]
+    [mouse-4] [down-mouse-4] [drag-mouse-4] [double-mouse-4] [triple-mouse-4]
+    [mouse-5] [down-mouse-5] [drag-mouse-5] [double-mouse-5] [triple-mouse-5])
+  "All of the mouse-related keybindings that Emacs recognizes.")
+
+(defconst constants/fill-column 80
+  "Variable used to set the defaults for wrapping, highlighting, etc.")
+
+(provide 'constants)
+;;; constants.el ends here
diff --git a/configs/shared/.emacs.d/wpc/cycle.el b/configs/shared/.emacs.d/wpc/cycle.el
new file mode 100644
index 0000000000..762488887d
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/cycle.el
@@ -0,0 +1,105 @@
+;;; cycle.el --- Simple module for working with cycles. -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Something like this may already exist, but I'm having trouble finding it, and
+;; I think writing my own is a nice exercise for learning more Elisp.
+
+;;; Code:
+
+(require 'struct)
+(require 'math)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Wish list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; - TODO: Provide immutable variant.
+;; - TODO: Replace mutable consumption with immutable variant.
+;; - TODO: Replace indexing with (math/mod current cycle).
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; `current-index' tracks the current index
+;; `xs' is the original list
+(cl-defstruct cycle current-index xs)
+
+(defun cycle/new ()
+  "Create an empty cycle."
+  (make-cycle :current-index 0
+              :xs '()))
+
+(defun cycle/from-list (xs)
+  "Create a cycle from a list of `XS'."
+  (make-cycle :current-index 0
+              :xs xs))
+
+(defun cycle/to-list (xs)
+  "Return the list representation of a cycle, XS."
+  (cycle-xs xs))
+
+(defun next-index<- (lo hi x)
+  "Return the next index in a cycle when moving downwards.
+- `LO' is the lower bound.
+- `HI' is the upper bound.
+- `X' is the current index."
+  (if (< (- x 1) lo)
+      (- hi 1)
+    (- x 1)))
+
+(defun next-index-> (lo hi x)
+  "Return the next index in a cycle when moving upwards.
+- `LO' is the lower bound.
+- `HI' is the upper bound.
+- `X' is the current index."
+  (if (>= (+ 1 x) hi)
+      lo
+    (+ 1 x)))
+
+(defun cycle/prev (cycle)
+  "Return the previous value in `CYCLE' and update `current-index'."
+  (let* ((current-index (cycle-current-index cycle))
+         (next-index (next-index<- 0 (cycle/count cycle) current-index)))
+    (setf (cycle-current-index cycle) next-index)
+    (nth next-index (cycle-xs cycle))))
+
+(defun cycle/next (cycle)
+  "Return the next value in `CYCLE' and update `current-index'."
+  (let* ((current-index (cycle-current-index cycle))
+         (next-index (next-index-> 0 (cycle/count cycle) current-index)))
+    (setf (cycle-current-index cycle) next-index)
+    (nth next-index (cycle-xs cycle))))
+
+(defun cycle/current (cycle)
+  "Return the current value in `CYCLE'."
+  (nth (cycle-current-index cycle) (cycle-xs cycle)))
+
+(defun cycle/count (cycle)
+  "Return the length of `xs' in `CYCLE'."
+  (length (cycle-xs cycle)))
+
+(defun cycle/jump (i cycle)
+  "Jump to the I index of CYCLE."
+  (setf (cycle-current-index cycle)
+        (math/mod i (cycle/count cycle)))
+  cycle)
+
+(defun cycle/focus (p cycle)
+  "Focus the element in CYCLE for which predicate, P, is t."
+  (let ((i (->> cycle
+                cycle-xs
+                (-find-index p))))
+    (if i
+        (cycle/jump i cycle)
+      (error "No element in cycle matches predicate"))))
+
+(defun cycle/contains? (x xs)
+  "Return t if cycle, XS, has member X."
+  (->> xs
+       cycle-xs
+       (list/contains? x)))
+
+(provide 'cycle)
+;;; cycle.el ends here
diff --git a/configs/shared/.emacs.d/wpc/device.el b/configs/shared/.emacs.d/wpc/device.el
new file mode 100644
index 0000000000..4d79214c37
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/device.el
@@ -0,0 +1,38 @@
+;;; device.el --- Physical device information -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Functions for querying device information.
+
+;;; Code:
+
+(require 'dash)
+(require 'alist)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst device/hostname->device
+  '(("wpcarro2" . work-laptop)
+    ("wpcarro.lon.corp.google.com" . work-desktop))
+  "Mapping hostname to a device symbol.")
+
+;; TODO: Should I generate these predicates?
+
+(defun device/classify ()
+  "Return the device symbol for the current host or nil if not supported."
+  (alist/get system-name device/hostname->device))
+
+(defun device/work-laptop? ()
+  "Return t if current device is work laptop."
+  (equal 'work-laptop
+         (device/classify)))
+
+(defun device/work-desktop? ()
+  "Return t if current device is work desktop."
+  (equal 'work-desktop
+         (device/classify)))
+
+(provide 'device)
+;;; device.el ends here
diff --git a/configs/shared/.emacs.d/wpc/display.el b/configs/shared/.emacs.d/wpc/display.el
new file mode 100644
index 0000000000..716f3e5f57
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/display.el
@@ -0,0 +1,47 @@
+;;; display.el --- Working with single or multiple displays -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Mostly wrappers around xrandr.
+;;
+;; TODO: Look into autorandr to see if it could be useful.
+;;
+;; Troubleshooting:
+;; The following commands help me when I (infrequently) interact with xrandr.
+;; - xrandr --listmonitors
+;; - xrandr --query
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Consider if this logic should be conditioned by `device/work-laptop?'.
+(defconst display/primary "eDP-1"
+  "The xrandr identifier for my primary screen (on work laptop).")
+
+(defconst display/4k "HDMI-1"
+  "The xrandr identifer for my 4K monitor.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun display/enable-4k ()
+  "Attempt to connect to my 4K monitor."
+  (interactive)
+  (shell-command
+   (string/format "xrandr --output %s --dpi 144 --auto --right-of %s"
+                  display/4k
+                  display/primary)))
+
+(defun display/disable-4k ()
+  "Disconnect from the 4K monitor."
+  (interactive)
+  (shell-command
+   (string/format "xrandr --output %s --off"
+                  display/4k)))
+
+(provide 'display)
+;;; display.el ends here
diff --git a/configs/shared/.emacs.d/wpc/do.el b/configs/shared/.emacs.d/wpc/do.el
new file mode 100644
index 0000000000..7dc2b260fd
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/do.el
@@ -0,0 +1,54 @@
+;;; do.el --- Small assertion library for Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Assertion library inspired by Elixir's core testing library.
+;;
+;; The goal here is to create this module without relying on other non-core
+;; Elisp libraries.  I will attempt to do this as long as I'm not sacrificing
+;; the readability of this code nor the ease at which it can be written.
+;;
+;; A note on testing:
+;; Another goal with this library is to blur the line between testing code and
+;; runtime code.  Developers should ideally be using `do/assert' and `do/refute'
+;; in their library code.  Because of this, I'm avoiding referring
+;; to the notion of testing in the names of these functions.
+;;
+;; Hypothesis:
+;; The lower the friction is for writing tests, the more likely people will
+;; write tests.
+
+;; TODO: Support better error messages, which might include information about
+;; line numbers in source code where the assertion failed.
+
+;; TODO: Consider offering the ability to have some of these functions compile
+;; to nothing at runtime if developers want to use them while developing without
+;; incurring the costs at runtime.
+
+;; TODO: Consider using this module instead of prelude.el.  Right now, I'm
+;; having troubling preferring one to the other.  The benefit of this module is
+;; that it's independent of prelude, but that might also be a downside, since
+;; the messaging that asserting should be a critical part of any core library
+;; like prelude.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro do/assert (x)
+  "Errors unless X is t.
+These are strict assertions and purposely do not rely on truthiness."
+  (let ((as-string (format "%s" x)))
+    `(unless (equal t ,x)
+       (error (concat "Assertion failed: " ,as-string)))))
+
+(defmacro do/refute (x)
+  "Errors unless X is nil."
+  (let ((as-string (format "%s" x)))
+    `(unless (eq nil ,x)
+       (error (concat "Refutation failed: " ,as-string)))))
+
+(provide 'do)
+;;; do.el ends here
diff --git a/configs/shared/.emacs.d/wpc/dotfiles.el b/configs/shared/.emacs.d/wpc/dotfiles.el
new file mode 100644
index 0000000000..3ee99196bd
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/dotfiles.el
@@ -0,0 +1,45 @@
+;;; dotfiles.el --- Elisp to make dotfile management -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Quickly edit commonly used files.
+
+;;; Code:
+
+(require 'macros)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; API
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst dotfiles/install-kbds? t
+  "When t, install the keybindings.")
+
+(defconst dotfiles/whitelist
+  '(("compton" . "~/.config/compton.conf")
+    ("dotfiles" . "~/Dropbox/dotfiles/")
+    ("functions" . "~/functions.zsh")
+    ("aliases" . "~/aliases.zsh")
+    ("variables" . "~/variables.zsh")
+    ("Xresources" . "~/.Xresources.shared")
+    ("tmux" . "~/.tmux.conf")
+    ("i3" . "~/.config/i3/config") ;; TODO: Remove this one day.
+    ("zshrc" . "~/.zshrc")
+    ("configuration.nix" . "~/Dropbox/programming/nixify/configuration.nix")
+    ("init.el" . "~/.emacs.d/init.el")
+    ("init.vim" . "~/.config/nvim/init.vim"))
+  "Dotfiles that I commonly edit.")
+
+(defun dotfiles/edit ()
+  "Select a dotfile from ivy and edit it in an Emacs buffer."
+  (interactive)
+  (ivy-read
+   "Dotfile: "
+   dotfiles/whitelist
+   :action (>> cdr find-file)))
+
+(when dotfiles/install-kbds?
+  (evil-leader/set-key "J" #'dotfiles/edit))
+
+(provide 'dotfiles)
+;;; dotfiles.el ends here
diff --git a/configs/shared/.emacs.d/wpc/dotted.el b/configs/shared/.emacs.d/wpc/dotted.el
new file mode 100644
index 0000000000..90ef39f92e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/dotted.el
@@ -0,0 +1,49 @@
+;;; dotted.el --- Working with dotted pairs in Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Part of my primitives library extensions in Elisp.  Contrast my primitives
+;; with the wrapper extensions that I provide, which expose immutable variants
+;; of data structures like an list, alist, tuple, as well as quasi-typeclasses
+;; like sequence, etc.
+
+;;; Code:
+
+(require 'prelude)
+(require 'macros)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defun dotted/new (&optional a b)
+  "Create a new dotted pair (i.e. cons cell)."
+  (cons a b))
+
+(defun dotted/instance? (x)
+  "Return t if X is a dotted pair."
+  (let ((b (cdr x)))
+    (and b (atom b))))
+
+(defun dotted/first (x)
+  "Return the first element of X."
+  (car x))
+
+(defun dotted/second (x)
+  "Return the second element of X."
+  (cdr x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(progn
+  (prelude/assert
+   (equal '(fname . "Bob") (dotted/new 'fname "Bob")))
+  (prelude/assert
+   (dotted/instance? '(one . two)))
+  (prelude/refute
+   (dotted/instance? '(1 2 3))))
+
+(provide 'dotted)
+;;; dotted.el ends here
diff --git a/configs/shared/.emacs.d/wpc/entr.el b/configs/shared/.emacs.d/wpc/entr.el
new file mode 100644
index 0000000000..ac2a5812c3
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/entr.el
@@ -0,0 +1,115 @@
+;;; entr.el --- Working with terminals and entr -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Help make watch commands easier.
+;;
+;; This should be entirely temporary because in reality we should be able to use
+;; Emacs's buffer watching abilities to run commands.
+;; TODO: Explore Emacs integration that obviates `entr`.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'f)
+(require 'buffer)
+(require 'prelude)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support a generic file-watcher for commonly used languages.
+(defconst entr/major-mode->save-handler
+  '((python-mode . entr/handle-python3))
+  "Mapping of language to the `after-save-hook' function it should register.")
+
+(defun entr/shell-command-to-buffer (cmd name)
+  "Run CMD in a shell and output to the buffer NAME.
+The buffer is a find-or-create operation.
+The buffer is erased between runs with `erase-buffer'."
+  (let ((b (buffer/find-or-create name)))
+    (with-current-buffer b (erase-buffer))
+    (shell-command cmd b)
+    (buffer/show b)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Python
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: This should be a top-level function.
+(defconst entr/handle-python3
+  (lambda ()
+    (entr/shell-command-to-buffer
+     (format "python3 %s" (buffer-file-name))
+     "*python3*"))
+  "Function that is registered as the `after-save-hook' for python3.")
+
+(defun entr/register-python3 ()
+  "Register a buffer-local `after-save-hook' for calling python3 with filename."
+  (interactive)
+  (add-hook 'after-save-hook entr/handle-python3 nil t))
+
+(defun entr/deregister-python3 ()
+  "Remove the buffer-local `after-save-hook' for python3."
+  (interactive)
+  (remove-hook 'after-save-hook entr/handle-python3 t))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Protobuf
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun entr/format-protobuf ()
+  "Formats a protobuf buffer."
+  (call-interactively #'clang-format))
+
+;; TODO: Run this automatically with .proto file extensions.  Do this after
+;; verifying that `clang-format' complies with Google's style guide.
+(defun entr/register-protobuf ()
+  "Register a buffer-local `before-save-hook' for formatting protobuf buffers."
+  (interactive)
+  (add-hook
+   'before-save-hook
+   #'entr/format-protobuf
+   nil
+   t))
+
+;; TODO: Is there an interactive way to remove hooks in Emacs?
+(defun entr/deregister-protobuf ()
+  "Remove the buffer-local `before-save-hook' for protobuf."
+  (interactive)
+  (remove-hook
+   'before-save-hook
+   #'entr/format-protobuf
+   t))
+
+;; TODO: Support this.  Currently the `intern' call is the problem.
+;; (defun entr/ivy-remove-hook (hook)
+;;   "Use Counsel to remove a handler from HOOK."
+;;   (interactive)
+;;   (ivy-read
+;;    "Remove hook: "
+;;    (intern (prelude/prompt "Hook name: "))
+;;    :action (lambda (x) (message x))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun entr/command (command)
+  "Create a terminal instance with entr running COMMAND.
+COMMAND is a function that is called with the current filename."
+  ;; Algorithm:
+  ;; - Get buffer's filename.
+  ;; - Open terminator running: `echo entr <filename> | entr <command>`.
+  (interactive)
+  (with-current-buffer (current-buffer)
+      (let ((filename (buffer-file-name)))
+        (prelude/inspect
+         (format "echo %s | entr %s" filename (funcall command filename))))))
+
+(provide 'entr)
+;;; entr.el ends here
diff --git a/configs/shared/.emacs.d/wpc/enum.el b/configs/shared/.emacs.d/wpc/enum.el
new file mode 100644
index 0000000000..078e797209
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/enum.el
@@ -0,0 +1,98 @@
+;;; enum.el --- Enumerable protocol for Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Heavily influenced by Elixir.
+
+;; I will not be implement every function in the Enum library, since I don't
+;; need every function.  Some of the streaming functionality may prove difficult
+;; to write in Elisp.  We shall see.
+
+;; TODO: Implement the following functions:
+;; - all?/2
+;; - any?/2
+;; - at/3
+;; - chunk_by/2
+;; - chunk_every/{2,3,4}
+;; - chunk_while/4
+;; - concat/1
+;; - concat/2
+;; - count/{1,2}
+;; - dedup/1 # prefer calling this function dedupe
+;; - dedup_by/2 # same as above
+;; - drop/2
+;; - drop_every/2
+;; - drop_while/2
+;; - each/2
+;; - empty?/1
+;; - fetch/2
+;; - fetch!/2
+;; - filter/2
+;; - find/3
+;; - find_index/2
+;; - find_value/3
+;; - flat_map/2
+;; - flat_map_reduce/3
+;; - group_by/3
+;; - intersperse/2
+;; - into/{2,3}
+;; - join/2
+;; - map/2
+;; - map_every/3
+;; - map_join/3
+;; - map_reduce/3
+;; - max/2
+;; - max_by/3
+;; - member?/2 # consider calling this contains?
+;; - min/2
+;; - min_by/2
+;; - min_max/2 # This is a great function because of O(n) time.
+;; - min_max_by/3
+;; - random/1 # Consider just sample with num=1
+;; - reduce/{2,3}
+;; - reduce_while/3
+;; - reject/2
+;; - reverse/{1,2}
+;; - reverse_slice/3
+;; - scan/{2,3}
+;; - shuffle/1
+;; - slice/{2,3}
+;; - sort/{1,2}
+;; - sort/2
+;; - sort_by/3
+;; - split/2
+;; - split_while/2
+;; - split_with/2
+;; - sum/1
+;; - take/2
+;; - take_every/2
+;; - take_random/2 # prefer calling this function sample
+;; - take_while/2
+;; - to_list/1
+;; - uniq/1 # prefer calling this unique
+;; - uniq_by/2 # prefer calling this unique-by
+;; - unzip/1
+;; - with_index/2
+;; - zip/{1,2}
+
+;; TODO: Consider how to handle dispatching by type.
+
+;; TODO: Which types should be supported herein?
+;; - linked-lists
+;; - associative-lists
+;; - cycles
+
+;; Warning: This module is a total work-in-progress, and it's quite possible
+;; that I may never even finish it.
+
+;;; Code:
+
+(defun enum/count (xs)
+  "Return the number of elements in `XS'."
+  (cond
+   ((alist/instance? xs) (alist/count xs))
+   ((list/instance?  xs) (list/length xs)))
+  )
+
+(provide 'enum)
+;;; enum.el ends here
diff --git a/configs/shared/.emacs.d/wpc/fonts.el b/configs/shared/.emacs.d/wpc/fonts.el
new file mode 100644
index 0000000000..fca544a4c3
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/fonts.el
@@ -0,0 +1,148 @@
+;;; fonts.el --- Font preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Control my font preferences with ELisp.
+
+;;; Code:
+
+;; TODO: `defcustom' font-size.
+;; TODO: `defcustom' fonts.
+;; TODO: Remove wpc/ namespace.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'cycle)
+(require 'device)
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Troubleshoot why "8" appears so large on my desktop.
+
+;; TODO: Consider having a different font size when I'm using my 4K monitor.
+
+(defconst fonts/size
+  (pcase (device/classify)
+    ('work-laptop "12")
+    ('work-desktop "8"))
+  "My preferred default font-size, which is device specific.")
+
+(defconst fonts/keybindings? t
+  "Install the keybindings when non-nil.")
+
+(defconst fonts/size-step 10
+  "The amount (%) by which to increase or decrease a font.")
+
+(defconst fonts/hacker-news-recommendations
+  '("APL385 Unicode"
+    "Go Mono"
+    "Sudo"
+    "Monoid"
+    "Input Mono Medium" ;; NOTE: Also "Input Mono Thin" is nice.
+    )
+  "List of fonts optimized for programming I found in a HN article.")
+
+(defconst fonts/whitelist
+  (cycle/from-list
+   (list/concat
+    fonts/hacker-news-recommendations
+    '("Monospace"
+      "Operator Mono Light"
+      "Courier"
+      "Andale Mono"
+      "Source Code Pro"
+      "Terminus")))
+  "This is a list of my preferred fonts.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: fonts and fonts/whitelist make it difficult to name functions like
+;; fonts/set as a generic Emacs function vs choosing a font from the whitelist.
+
+(cl-defun fonts/cycle (&key forward?)
+  "Cycle forwards when `FORWARD?' non-nil."
+  (let ((font (if forward?
+                  (cycle/next fonts/whitelist)
+                (cycle/prev fonts/whitelist))))
+    (message (s-concat "Active font: " font))
+    (fonts/set (fonts/fontify font))))
+
+(defun fonts/next ()
+  "Quickly cycle through preferred fonts."
+  (interactive)
+  (fonts/cycle :forward? t))
+
+(defun fonts/prev ()
+  "Quickly cycle through preferred fonts."
+  (interactive)
+  (fonts/cycle :forward? nil))
+
+(defun fonts/set (font &optional size)
+  "Change the font to `FONT' with option integer, SIZE, in pixels."
+  (if (maybe/some? size)
+      (set-frame-font (string/format "%s %s" font size) nil t)
+    (set-frame-font font nil t)))
+
+(defun fonts/whitelist-set (font)
+  "Focuses the FONT in the `fonts/whitelist' cycle.
+The size of the font is determined by `fonts/size'."
+  (prelude/assert (cycle/contains? font fonts/whitelist))
+  (cycle/focus (lambda (x) (equal x font)) fonts/whitelist)
+  (fonts/set (fonts/current) fonts/size))
+
+(defun fonts/ivy-select ()
+  "Select a font from an ivy prompt."
+  (interactive)
+  (fonts/whitelist-set
+   (ivy-read "Font: " (cycle/to-list fonts/whitelist))))
+
+(defun fonts/print-current ()
+  "Message the currently enabled font."
+  (interactive)
+  (message
+   (string/format "[fonts] Current font: \"%s\""
+                  (fonts/current))))
+
+(defun fonts/current ()
+  "Return the currently enabled font."
+  (cycle/current fonts/whitelist))
+
+(defun fonts/increase-size ()
+  "Increase font size."
+  (interactive)
+  (->> (face-attribute 'default :height)
+       (+ fonts/size-step)
+       (set-face-attribute 'default (selected-frame) :height)))
+
+(defun fonts/decrease-size ()
+  "Decrease font size."
+  (interactive)
+  (->> (face-attribute 'default :height)
+       (+ (- fonts/size-step))
+       (set-face-attribute 'default (selected-frame) :height)))
+
+(defun fonts/reset-size ()
+  "Restore font size to its default value."
+  (interactive)
+  (fonts/whitelist-set (fonts/current)))
+
+(when fonts/keybindings?
+  (progn
+    (evil-leader/set-key
+      "Ff" #'fonts/next
+      "Pf" #'fonts/prev)
+    (general-define-key "s-9" #'fonts/ivy-select)
+    (general-define-key "s-0" #'fonts/reset-size)
+    (general-define-key "s-j" #'fonts/decrease-size)
+    (general-define-key "s-k" #'fonts/increase-size)))
+
+(provide 'fonts)
+;;; fonts.el ends here
diff --git a/configs/shared/.emacs.d/wpc/fs.el b/configs/shared/.emacs.d/wpc/fs.el
new file mode 100644
index 0000000000..adc331d176
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/fs.el
@@ -0,0 +1,59 @@
+;;; fs.el --- Make working with the filesystem easier -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Ergonomic alternatives for working with the filesystem.
+
+;; Dependencies
+(require 'f)
+
+;;; Code:
+
+(defun fs/ensure-file (path)
+  "Ensure that a file and its directories in `PATH' exist.
+Will error for inputs with a trailing slash."
+  (when (s-ends-with? "/" path)
+    (error (format "Input path has trailing slash: %s" path)))
+  (->> path
+       f-dirname
+       fs/ensure-dir)
+  (f-touch path))
+
+(f-dirname "/tmp/a/b/file.txt")
+
+(defun fs/ensure-dir (path)
+  "Ensure that a directory and its ancestor directories in `PATH' exist."
+  (->> path
+       f-split
+       (apply #'f-mkdir)))
+
+(defun fs/ls (dir &optional full-path?)
+  "List the files in `DIR' one-level deep.
+Should behave similarly in spirit to the Unix command, ls.
+If `FULL-PATH?' is set, return the full-path of the files."
+  (-drop 2 (directory-files dir full-path?)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support `refute' function / macro.
+(ert-deftest fs/test/ensure-file ()
+  (let ((file "/tmp/file/a/b/c/file.txt"))
+    ;; Ensure this file doesn't exist first to prevent false-positives.
+    (f-delete file t)
+    (fs/ensure-file file)
+    (should (and (f-exists? file)
+                 (f-file? file)))))
+
+(ert-deftest fs/test/ensure-dir ()
+  (let ((dir "/tmp/dir/a/b/c"))
+    ;; Ensure the directory doesn't exist.
+    (f-delete dir t)
+    (fs/ensure-dir dir)
+    (should (and (f-exists? dir)
+                 (f-dir? dir)))))
+
+(provide 'fs)
+;;; fs.el ends here
diff --git a/configs/shared/.emacs.d/wpc/functions.el b/configs/shared/.emacs.d/wpc/functions.el
new file mode 100644
index 0000000000..33e256be59
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/functions.el
@@ -0,0 +1,133 @@
+;; functions.el --- Helper functions for my Emacs development -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This file hopefully contains friendly APIs that making ELisp development more
+;; enjoyable.
+
+;; TODO: Break these out into separate modules.
+
+;;; Code:
+(defun wpc/evil-window-vsplit-right ()
+  (interactive)
+  (evil-window-vsplit)
+  (windmove-right))
+
+(defun wpc/evil-window-split-down ()
+  (interactive)
+  (evil-window-split)
+  (windmove-down))
+
+(defun wpc/reindent-defun-and-align-clojure-map ()
+  (interactive)
+  (call-interactively #'paredit-reindent-defun)
+  (call-interactively #'clojure-align))
+
+(defun wpc/find-file ()
+  "Prefer project-based file-finding if inside of project; otherwise gracefully fallback."
+  (interactive)
+  (with-current-buffer (current-buffer)
+    (if (projectile-project-p)
+        (call-interactively #'counsel-projectile-find-file)
+      (call-interactively #'find-file))))
+
+(defun wpc/find-or-create-js-test ()
+  (->> buffer-file-name
+       (s-chop-suffix ".js")
+       (s-append ".test.js")
+       (find-file)))
+
+(defun wpc/find-or-create-js-module ()
+  (->> buffer-file-name
+       (s-chop-suffix ".test.js")
+       (s-append ".js")
+       (find-file)))
+
+(defun wpc/find-or-create-js-store ()
+  (->> buffer-file-name
+       (s-replace "index.js" "store.js")
+       (find-file)))
+
+(defun wpc/find-or-create-js-component ()
+  (->> buffer-file-name
+       (s-replace "store.js" "index.js")
+       (find-file)))
+
+(defun wpc/toggle-between-js-test-and-module ()
+  "Toggle between a Javascript test or module."
+  (interactive)
+  (if (s-ends-with? ".test.js" buffer-file-name)
+      (wpc/find-or-create-js-module)
+    (if (s-ends-with? ".js" buffer-file-name)
+        (wpc/find-or-create-js-test)
+      (message "Not in a Javascript file. Exiting..."))))
+
+(defun wpc/toggle-between-js-component-and-store ()
+  "Toggle between a React component and its Redux store."
+  (interactive)
+  (if (s-ends-with? "index.js" buffer-file-name)
+      (wpc/find-or-create-js-store)
+    (if (or (s-ends-with? "store.js" buffer-file-name)
+            (s-ends-with? "store.test.js" buffer-file-name))
+        (wpc/find-or-create-js-component)
+      (message "Not in a React/Redux file. Exiting..."))))
+
+(defun wpc/read-file-as-string (filename)
+  (with-temp-buffer
+    (insert-file-contents filename)
+    (s-trim (buffer-string))))
+
+(defun wpc/create-snippet ()
+  "Creates a window split and then opens the Yasnippet editor."
+  (interactive)
+  (evil-window-vsplit)
+  (call-interactively #'yas-new-snippet))
+
+(defun wpc/find-file-split (filename)
+  "Creates a window split and then edits `filename'."
+  (interactive)
+  (evil-window-vsplit)
+  (find-file filename))
+
+(defun wpc/jump-to-parent-file ()
+  "Jumps to a React store or component's parent file. Useful for store or index file."
+  (interactive)
+  (-> buffer-file-name
+      f-dirname
+      (f-join "..")
+      (f-join (f-filename buffer-file-name))
+      find-file))
+
+(defun wpc/add-earmuffs (x)
+  "Returns X surrounded by asterisks."
+  (format "*%s*" x))
+
+(defun wpc/put-file-name-on-clipboard ()
+  "Put the current file name on the clipboard"
+  (interactive)
+  (let ((filename (if (equal major-mode 'dired-mode)
+                      default-directory
+                    (buffer-file-name))))
+    (when filename
+      (with-temp-buffer
+        (insert filename)
+        (clipboard-kill-region (point-min) (point-max)))
+      (message filename))))
+
+(s-replace "/" "x" "a/b/c")
+
+(defun wpc/evil-replace-under-point ()
+  "Faster than typing %s//thing/g."
+  (interactive)
+  (let ((term (s-replace "/" "\\/" (symbol/to-string (symbol-at-point)))))
+    (save-excursion
+      (evil-ex (concat "%s/\\b" term "\\b/")))))
+
+(defun buffer-dirname ()
+  "Return the directory name of the current buffer as a string."
+  (->> buffer-file-name
+       f-dirname
+       f-filename))
+
+(provide 'functions)
+;;; functions.el ends here
diff --git a/configs/shared/.emacs.d/wpc/google-stuff.el b/configs/shared/.emacs.d/wpc/google-stuff.el
new file mode 100644
index 0000000000..4f4fe635a3
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/google-stuff.el
@@ -0,0 +1,183 @@
+;;; google-stuff.el --- Working with Google infrastructure from Emacs -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Some of this is just encoding my learnings as notes in Elisp format.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'f)
+(require 'ivy-helpers)
+(require 'evil-leader)
+(require 'maybe)
+(require 'device)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Ensure a consistent and deliberate usage of `defvar', `defconst', and
+;; `defcustom' across all Elisp modules.
+(defcustom google-stuff/install-kbds? t
+  "When t, install the keybindings defined herein.")
+
+;; Definitions as explained by the highly knowledgeable Matthew (i.e. mjo@)
+(defconst google-stuff/definitions
+  '(
+    ;; command-line tools
+    ("gcert"  . "Requests a CorpSSH certificate.")
+    ("glogin" . "SSO (i.e. Single Sign-On) cookie.")
+    ("googlenetworkaccess" . "Device certificate that gives users a certificate
+to access to the Google corp network.")
+    ("prodaccess" . "Sets up a LOAS session on Goobuntu.")
+    ;; general wtfs
+    ("LOAS" . "Distributed authentication service used by jobs in production and
+corp to authenticate each other. It's more efficient than SSL and works with
+Stubby.")
+    ))
+
+;; TODO: Straighten out fig, citc, google3 and have modules for each.
+
+;; TODO: Move this to a google3.el module.
+(defconst google-stuff/root
+  "/google/src/cloud/wpcarro"
+  "The root directory to access google3.")
+
+;; TODO: Find a fast way to generate this.
+(defconst google-stuff/citc-clients
+  '("auto-consult"
+    "ac-skeleton")
+  "A list of my active CitC clients.")
+
+
+;; TODO: Can this be sourced from ~/.g4d?
+(defconst google-stuff/citc-aliases
+  '(("escalations" . "/google3/corp/gtech/pto/tda/beacons_extension")
+    ("spewall_fe" . "/google3/alkali/apps/speakeasydashboard")
+    ("spewall_be" . "/google3/java/com/google/alkali/applications/speakeasydashboard")
+    ("spewall_protos" . "/google3/google/internal/alkali/applications/speakeasydashboard")
+    ("spewall_tests" . "/google3/javatests/com/google/alkali/applications/speakeasydashboard")
+    ("gti" . "/google3/experimental/engedu/gti/projects/week20190422/mtv/Team10")
+    ("authwf" . "/google3/customer_support/automation/workflow")
+    ("redwood" . "/google3/customer_support/kms/redwood/ui")
+    ("wf-fe" . "/google3/customer_support/kms/redwood/ui/client/components/item/workflow_editor")
+    ("ac" . "/google3/google/internal/alkali/applications/casesconsultservice")
+    ("ac-server" . "/google3/java/com/google/alkali/applications/casesconsultservice/server/")
+    ("ac-server (tests)" . "/google3/javatests/com/google/alkali/applications/casesconsultservice/server/"))
+  "Mapping of a label to commonly visited locations in Google3.")
+
+
+(defvar google-stuff/active-citc-client nil
+  "Currently active CitC client.")
+
+(defun google-stuff/depot-prefix ()
+  "Return the current prefix for //depot/google3."
+  (string/format "/google/src/cloud/wpcarro/%s/google3/"
+                 google-stuff/active-citc-client))
+
+(defun google-stuff/cs-url ()
+  "Return the code-search URL for the current buffer and line number."
+  (string/format "cs.corp.google.com/piper///depot/google3/%s?l=%s"
+                 (s-chop-prefix
+                  (google-stuff/depot-prefix)
+                  (buffer-file-name))
+                 (line-number-at-pos)))
+
+(defun google-stuff/copy-cs-url ()
+  "Copy the current file and line-position to the system clipboard."
+  (interactive)
+  (clipboard/copy (google-stuff/cs-url)))
+
+(defun google-stuff/open-buffer-in-cs ()
+  "Open the current file in Google's CodeSearch."
+  (interactive)
+  (shell-command
+   (string/format "google-chrome '%s'"
+                  (google-stuff/cs-url)
+                  (line-number-at-pos))))
+
+;; TODO: As a naming convention, should I prefer ivy or select? Or counsel?
+(defun google-stuff/select-citc-client ()
+  "Set `google-stuff/active-citc-client' with counsel."
+  (interactive)
+  (setq google-stuff/active-citc-client
+        (ivy-read "CitC Client: " google-stuff/citc-clients)))
+
+(defun google-stuff/remote-buffer? ()
+  "Return t if buffer is one accessed via Tramp."
+  (with-current-buffer (current-buffer)
+    (if (file-remote-p default-directory)
+        t
+      nil)))
+
+(defun google-stuff/jump-to-citc-alias ()
+  "Use `find-file' to open an alias registered in `google-stuff/citc-aliases'.
+When on a corporate laptop, remote connections are made using Tramp."
+  (interactive)
+  (when (maybe/nil? google-stuff/active-citc-client)
+    (call-interactively #'google-stuff/select-citc-client))
+  (ivy-helpers/kv
+   "Jump to CitC Alias: "
+   google-stuff/citc-aliases
+   (lambda (k v)
+     (->> v
+          ;; If I don't remove the leading slash, `f-join' won't return a valid
+          ;; path.
+          (s-chop-prefix "/")
+          (f-join google-stuff/root
+                  google-stuff/active-citc-client)
+          (s-prepend (if (device/work-laptop?) "/ssh:wpcarro@desktop:" ""))
+          find-file))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Stuff I learned reading go/emacs
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Fig
+;; TODO: Make sure there are Evil-compatible KBDs for `fig-status'.
+;; (require 'google-fig)
+
+;; This allows `find-file' handle "//depot/google3/devtools/editors/".
+(require 'p4-files)
+(p4-enable-file-name-handler)
+
+;; Blaze Support
+;; - `google3-compile-current-file' is an excellent command!
+
+;; google3-eglot (uses CiderLSP)
+;; TODO: Make sure the functionality is supported as advertised:
+;; - auto-completion
+;; - eglot-help-at-point for documentation.
+;; - goto-definition
+;; - `eglot-code-actions' fixits
+;; - `eglot-rename' refactoring
+(require 'google3-eglot)
+(google3-eglot-setup)
+
+;; CodeSearch
+;; TODO: Debug why this depends on google-piper and why I don't have that on my
+;; desktop.
+;; (require 'ivy-cs)
+
+;; Auto completion
+;; TODO: Is the part of or separate from google3-eglot?  Because google3-eglot
+;; advertises auto-completion support.
+(require 'google3-build-capf)
+(google3-build-capf-enable-completions)
+(add-to-list 'company-backends #'company-capf)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Keybindings
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(when google-stuff/install-kbds?
+  (evil-leader/set-key "Gs" #'fig-status)
+  (evil-leader/set-key "Cs" #'google-stuff/open-buffer-in-cs)
+  (evil-leader/set-key "jc" #'google-stuff/jump-to-citc-alias))
+
+(provide 'google-stuff)
+;;; google-stuff.el ends here
diff --git a/configs/shared/.emacs.d/wpc/google-tooling.el b/configs/shared/.emacs.d/wpc/google-tooling.el
new file mode 100644
index 0000000000..661df41d6c
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/google-tooling.el
@@ -0,0 +1,53 @@
+;;; google-tooling.el --- Better access to Google tooling -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+
+;; First, I must opine.  Feel free to skip this section.  In general, it seems
+;; that the average programmer's workflow suffer from what economists call
+;; "inelastic demand".  This means that any increase in price for something
+;; sends the demand plummeting.  Another way of phrasing this is that
+;; programmers are "price sensitive" when it comes to adopting new workflows.
+;;
+;; For us, any deviation from our "established" workflow feels costly.  This
+;; makes sense to me because programming is already taxing, so any additional
+;; taxation can feel unbearable.  Until programming changes dramatically, and we
+;; relieve our dependence on files and text for modeling complex applications,
+;; this inelastic demand will remain the status quo.  Therefore, it's critical
+;; to reduce the price of experimenting with new tools such that new, superior
+;; habits may form.  In this vain, this module attempts to surface "luxury
+;; tools" (i.e. dependency pruners, code linters, code formatters) via Emacs to
+;; reduce the price of experimenting with them.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'macros)
+
+;; TODO: Figure out whether or not to integrate with google-emacs.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst google-tooling/tools
+  '(("Depana" . "depana")
+    ("Build cleaner" . "build_cleaner")
+    ("Java formatter" . "google-java-format")
+    ("Proto formatter" . "clang-format"))
+  "Mapping of names of tools to the names of the executables that run them.")
+
+(use-package protobuf-mode
+  :config
+  (macros/support-file-extension "pb" protobuf-mode))
+
+;; TODO: Call blaze build, use Counsel to select an action, run that action on
+;; the nearest BUILD file.
+
+;; TODO: Call build-cleaner on the nearest BUILD file.
+
+(provide 'google-tooling)
+;;; google-tooling.el ends here
diff --git a/configs/shared/.emacs.d/wpc/graph.el b/configs/shared/.emacs.d/wpc/graph.el
new file mode 100644
index 0000000000..c68c308590
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/graph.el
@@ -0,0 +1,91 @@
+;;; graph.el --- Working with in-memory graphs -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;;
+;; Remember that there are optimal three ways to model a graph:
+;; 1. Edge List
+;; 2. Vertex Table (a.k.a. Neighbors Table)
+;; 3. Adjacency Matrix
+;;
+;; I may call these "Edges", "Neighbors", "Adjacencies" to avoid verbose naming.
+;; For now, I'm avoiding dealing with Adjacency Matrices as I don't have an
+;; immediate use-case for them.  This is subject to change.
+;;
+;; There are also hybrid representations of graphs that combine the three
+;; aforementioned models.  I believe Erlang's digraph module models graphs in
+;; Erlang Term Storage (i.e. ETS) this way.
+;; TODO: Verify this claim.
+;;
+;; Graphs can be weighted or unweighted.  They can also be directed or
+;; undirected.
+;; TODO: Create a table explaining all graph variants.
+;;
+;; TODO: Figure out the relationship of this module and tree.el, which should in
+;; principle overlap.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; For now, I'll support storing *either* neighbors or edges in the graph struct
+;; as long as both aren't set, since that introduces consistency issues.  I may
+;; want to handle that use-case in the future, but not now.
+(cl-defstruct graph neighbors edges)
+
+;; TODO: How do you find the starting point for a topo sort?
+(defun graph/sort (xs)
+  "Return a topological sort of XS.")
+
+(defun graph/from-edges (xs)
+  "Create a graph struct from the Edge List, XS.
+The user must pass in a valid Edge List since asserting on the shape of XS might
+  be expensive."
+  (make-graph :edges xs))
+
+(defun graph/from-neighbors (xs)
+  "Create a graph struct from a Neighbors Table, XS.
+The user must pass in a valid Neighbors Table since asserting on the shape of
+  XS might be expensive."
+  (make-graph :neighbors xs))
+
+(defun graph/instance? (xs)
+  "Return t if XS is a graph struct."
+  (graph-p xs))
+
+;; TODO: Model each of the mapping functions into an isomorphism.
+(defun graph/edges->neighbors (xs)
+  "Map Edge List, XS, into a Neighbors Table."
+  (prelude/assert (graph/instance? xs)))
+
+(defun graph/neighbors->edges (xs)
+  "Map Neighbors Table, XS, into an Edge List."
+  (prelude/assert (graph/instance? xs)))
+
+;; Below are three different models of the same unweighted, directed graph.
+
+(defvar graph/edges
+  '((a . b) (a . c) (a . e)
+    (b . c) (b . d)
+    (c . e)
+    (d . f)
+    (e . d) (e . f)))
+
+(defvar graph/neighbors
+  ((a b c e)
+   (b c d)
+   (c e)
+   (d f)
+   (e d g)
+   (f)))
+
+(provide 'graph)
+;;; graph.el ends here
diff --git a/configs/shared/.emacs.d/wpc/imdb.el b/configs/shared/.emacs.d/wpc/imdb.el
new file mode 100644
index 0000000000..2969da1409
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/imdb.el
@@ -0,0 +1,128 @@
+;;; imdb.el --- Internet Movie Database -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Some Elisp to help me pick movies more quickly.
+
+;;; Code:
+
+(require 'f)
+(require 'macros)
+(require 'pcre2el)
+(require 'random)
+(require 'maybe)
+
+;; TODO: How do you support types herein?
+(cl-defstruct movie
+  name
+  year
+  director
+  watched?)
+
+;; TODO: Support famous directors like:
+;; - Wes Anderson
+;; - Woody Allen
+;; - Tarantino
+;; - Coen Brothers
+;; - Alfonso Cauron
+;; - Alejandro González Iñárritu
+;; - Alfred Hitchcock
+;; - Stanley Kubrick
+
+;; TODO: Dump this into SQL.
+
+(defconst imdb/kubrick-films
+  (->> '((:watched? nil :year 1951 :name "Flying Padre")
+         (:watched? nil :year 1953 :name "Fear and Desire")
+         (:watched? nil :year 1953 :name "The Seafarers")
+         (:watched? nil :year 1955 :name "Killer's Kiss")
+         (:watched? nil :year 1956 :name "The Killing")
+         (:watched? nil :year 1957 :name "Paths of Glory")
+         (:watched? nil :year 1960 :name "Spartacus")
+         (:watched? nil :year 1962 :name "Lolita")
+         (:watched? nil :year 1964 :name "Dr. Strangelove")
+         (:watched? nil :year 1968 :name "2001: A Space Odyssey")
+         (:watched? t   :year 1971 :name "A Clockwork Orange")
+         (:watched? nil :year 1975 :name "Barry Lyndon")
+         (:watched? nil :year 1980 :name "The Shining")
+         (:watched? t   :year 1987 :name "Full Metal Jacket")
+         (:watched? nil :year 1999 :name "Eyes Wide Shut"))
+       (list/map (lambda (x)
+                   (make-movie :name (plist-get :name x)
+                               :year (plist-get :year x)
+                               :director "Stanley Kubrick"
+                               :watched? (plist-get :watched? x))))))
+
+(defconst imdb/non-top-250
+  (->> '("Doctor Zhivago"
+         )
+       (list/map #'imdb/new-movie)))
+
+(defun imdb/new-movie (name)
+  "Create a new movie with NAME."
+  (make-movie :name name
+              :year nil
+              :director nil
+              :watched? nil))
+
+(defun imdb/org->movie (line)
+  "Parse an org LINE into a movie struct."
+  (let ((match (s-match
+                (pcre-to-elisp "^\*\*\s(TODO|DONE)\s(.+)$")
+                line)))
+    (if (maybe/some? match)
+        (make-movie :name (list/get 2 match)
+                    :year nil
+                    :director nil
+                    :watched? (equal "DONE" (list/get 1 match)))
+      (error (s-concat "Parsing error: " line)))))
+
+;; TODO: Store these in a database or define them herein.
+(defun imdb/org->movies ()
+  "Parse entire IMDB org file into movie structs."
+  (->> "~/Dropbox/org/imdb_top_250.org"
+       f-read
+       (s-split "\n")
+       (-drop 1)
+       (list/filter (>> (s-starts-with? "** ")))
+       (list/map #'imdb/org->movie)))
+
+(defun imdb/watched? (movie)
+  "Return t if MOVIE has been watched."
+  (movie-watched? movie))
+
+(defconst imdb/movies (imdb/org->movies)
+  "Structs of all watched movies.")
+
+(defun imdb/unwatched ()
+  "Return list of unwatched movies."
+  (->> imdb/movies
+       (list/filter (lambda (x) (not (imdb/watched? x))))))
+
+(defun imdb/name (movie)
+  "Return name of MOVIE."
+  (movie-name movie))
+
+
+(defun imdb/suggest ()
+  "Randomly select movie from unwatched list."
+  (->> (imdb/unwatched)
+       (random/choice)
+       (imdb/name)))
+
+(defun imdb/unwatched-list ()
+  "Dump all unwatched movies into a list."
+  (f-write-text (->> (imdb/unwatched)
+                     (list/map #'imdb/name)
+                     (s-join "\n"))
+                'utf-8
+                "/tmp/unwatched.txt"))
+
+(macros/comment
+ (imdb/org->movies)
+ (imdb/unwatched-list)
+ (imdb/suggest)
+ )
+
+(provide 'imdb)
+;;; imdb.el ends here
diff --git a/configs/shared/.emacs.d/wpc/irc.el b/configs/shared/.emacs.d/wpc/irc.el
new file mode 100644
index 0000000000..3dfaabdb39
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/irc.el
@@ -0,0 +1,79 @@
+;;; irc.el --- Configuration for IRC chat -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Need to decide which client I will use for IRC.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'erc)
+(require 'cycle)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq erc-rename-buffers t)
+
+(defvar irc/channels-cycle
+  (cycle/from-list
+   '("#omg" "#london" "#panic" "#prod-team"))
+  "List of channels through which I can cycle.")
+
+;; Setting `erc-join-buffer' to 'bury prevents erc from stealing focus of the
+;; current buffer when it connects to IRC servers.
+(setq erc-join-buffer 'bury)
+
+(setq erc-autojoin-channels-alist
+      `(("corp.google.com" . ,(cycle/to-list irc/channels-cycle))))
+
+(defcustom irc/install-kbds? t
+  "When t, install the keybindings defined herein.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun irc/message (x)
+  "Print message X in a structured way."
+  (message (string/format "[irc.el] %s" x)))
+
+(defun irc/connect-to-google ()
+  "Connect to Google's Corp IRC using ERC."
+  (interactive)
+  (erc-ssl :server "irc.corp.google.com"
+           :port 6697
+           :nick "wpcarro"
+           :full-name "William Carroll"))
+
+(defun irc/next-channel ()
+  "Join the next channel in `irc/channels-cycle'."
+  (interactive)
+  (erc-join-channel
+   (cycle/next irc/channels-cycle))
+  (irc/message
+   (string/format "Current IRC channel: %s"
+                  (cycle/current irc/channels-cycle))))
+
+(defun irc/prev-channel ()
+  "Join the previous channel in `irc/channels-cycle'."
+  (interactive)
+  (erc-join-channel
+   (cycle/prev irc/channels-cycle))
+  (irc/message
+   (string/format "Current IRC channel: %s"
+                  (cycle/current irc/channels-cycle))))
+
+(when irc/install-kbds?
+  (general-define-key
+   :keymaps 'erc-mode-map
+   "<C-tab>" #'irc/next-channel
+   "<C-S-iso-lefttab>" #'irc/prev-channel))
+
+(provide 'irc)
+;;; irc.el ends here
diff --git a/configs/shared/.emacs.d/wpc/iso.el b/configs/shared/.emacs.d/wpc/iso.el
new file mode 100644
index 0000000000..1691a0daaa
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/iso.el
@@ -0,0 +1,95 @@
+;;; iso.el --- Isomorphisms in Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Providing basic isomorphisms to improve code quality.
+
+;;; Code:
+
+(require 'dotted)
+(require 'tuple)
+(require 'symbol)
+(require 'string)
+(require 'list)
+(require 'alist)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct iso to from x)
+
+(defconst iso/whitelist
+  '((dotted . tuple)
+    (symbol . string))
+  "Alist representing supported isomorphisms.")
+
+(defconst iso/vertices
+  (list/concat (alist/keys iso/whitelist)
+               (alist/values iso/whitelist))
+  "List of all of the vertices in the iso graph.")
+
+(defun iso/classify (x)
+  "Return type of X."
+  (cond
+   ((string/instance? x) 'string)
+   ((symbol/instance? x) 'symbol)
+   ((dotted/instance? x) 'dotted)
+   ((tuple/instance? x)  'tuple)))
+
+(cl-defun iso/exists? (to from)
+  "Return t if an isomorphism of TO to FROM exists."
+  ;; TODO: All of this can be improved modelling this with a graph.
+  (cond
+   ;; to -> from
+   ((list/contains? to (alist/keys iso/whitelist))
+    (list/contains? from (alist/values iso/whitelist)))
+   ;; from -> to
+   ((list/contains? from (alist/keys iso/whitelist))
+    (list/contains? to (alist/values iso/whitelist)))
+   ;; doesn't exist
+   (t nil)))
+
+(progn
+  (prelude/assert
+   (iso/exists? 'symbol 'string))
+  (prelude/assert
+   (iso/exists? 'dotted 'tuple))
+  (prelude/refute
+   (iso/exists? 'dotted 'symbol))
+  (prelude/refute
+   (iso/exists? 'symbol 'list)))
+
+;; TODO: Model this as a graph.
+(defconst iso/morphisms
+  '((string .
+            '(symbol #')
+     ))
+  (list (:from 'string :to 'symbol :fn #'intern)
+        (:from 'symbol :to 'string :fn #'symbol-name)
+        )
+  "")
+
+(defun iso/to (f x)
+  "Apply F to X's to."
+  (->> x
+       iso-to))
+
+(->> (iso/new "william" :to 'symbol)
+     (iso/as-to #'symbol-name)
+     )
+
+(cl-defun iso/new (x &keys to)
+  "Create a new isomorphism of X mapping to TO."
+  (let ((from (iso/classify x)))
+    (prelude/assert (iso/exists? to from))
+    (make-iso :from from
+              :to to
+              :x x)))
+
+(macros/comment
+ (iso/new "william" :to 'symbol)
+ (iso/new '(one . two) :to 'tuple))
+
+(provide 'iso)
+;;; iso.el ends here
diff --git a/configs/shared/.emacs.d/wpc/ivy-helpers.el b/configs/shared/.emacs.d/wpc/ivy-helpers.el
new file mode 100644
index 0000000000..c71a907a20
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/ivy-helpers.el
@@ -0,0 +1,31 @@
+;;; ivy-helpers.el --- More interfaces to ivy -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hopefully to improve my workflows.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'alist)
+(require 'tuple)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defun ivy-helpers/kv (prompt kv f)
+  "PROMPT users with the keys in KV and return its corresponding value.  Calls F
+with the key and value from KV."
+  (ivy-read
+   prompt
+   kv
+   :require-match t
+   :action (lambda (entry)
+             (funcall f (car entry) (cdr entry)))))
+
+;;; Code:
+(provide 'ivy-helpers)
+;;; ivy-helpers.el ends here
diff --git a/configs/shared/.emacs.d/wpc/kaomoji.el b/configs/shared/.emacs.d/wpc/kaomoji.el
new file mode 100644
index 0000000000..d6d509c146
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/kaomoji.el
@@ -0,0 +1,45 @@
+;;; kaomoji.el --- Supporting kaomoji usage -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Simple keyboards like this make life a bit better.
+
+;;; Code:
+
+(defvar kaomoji/install-kbds?
+  nil
+  "Set to t if you'd like the keybindings to be installed.")
+
+(defconst kaomoji/symbols '(("Joy" . "(⌒‿⌒)")
+                    ("Love" . "(ღ˘⌣˘ღ)")
+                    ("Sympathy" . "ヽ(~_~(・_・ )ゝ")
+                    ("Dissatisfaction" . "(>﹏<)")
+                    ("Anger" . "ヽ(‵﹏´)ノ")
+                    ("Hugging" . "(づ ̄ ³ ̄)づ")
+                    ("Hiding" . "┬┴┬┴┤( ͡° ͜ʖ├┬┴┬┴")
+                    ("Sleeping" . "(-_-) zzZ")
+                    ("Embarrassed" . "(×﹏×)")
+                    ("Shrug" . "ヽ(ー_ー )ノ"))
+  "Alist of human-readable emotions to the kaomoji.")
+
+;; TODO: Consider supporting a hydra for these.
+
+(defun kaomoji/select ()
+  "Interactively select a kaomoji and copy it to the clipboard."
+  (interactive)
+  (ivy-read
+   "Select a kaomoji: "
+   kaomoji/symbols
+   :action (lambda (entry)
+             (kill-new (cdr entry))
+             (alert "Copied to clipboard!"))))
+
+;; TODO: Define Hydra for all custom keyboards.
+;; TODO: Define a better keybinding in a different keymap.
+(when kaomoji/install-kbds?
+  (general-define-key
+   :keymaps 'global
+   "M-k" #'kaomoji/select))
+
+(provide 'kaomoji)
+;;; kaomoji.el ends here
diff --git a/configs/shared/.emacs.d/wpc/kbd.el b/configs/shared/.emacs.d/wpc/kbd.el
new file mode 100644
index 0000000000..49b346bc6e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/kbd.el
@@ -0,0 +1,90 @@
+;;; kbd.el --- Elisp keybinding -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; In order to stay organized, I'm attempting to dedicate KBD prefixes to
+;; specific functions.  I'm hoping I can be more deliberate with my keybinding
+;; choices this way.
+;;
+;; Terminology:
+;; For a more thorough overview of the terminology refer to `keybindings.md'
+;; file.  Here's a brief overview:
+;; - workspace: Anything concerning EXWM workspaces.
+;; - x11: Anything concerning X11 applications.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'alist)
+(require 'set)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst kbd/install-kbds? t
+  "When t, install keybindings defined herein.")
+
+(defconst kbd/prefixes
+  '((workspace . "s")
+    (x11 . "C-s"))
+  "Mapping of functions to designated keybinding prefixes to stay organized.")
+
+;; Assert that no keybindings are colliding.
+(prelude/assert
+ (= (alist/count kbd/prefixes)
+    (->> kbd/prefixes
+         alist/values
+         set/from-list
+         set/count)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun kbd/raw (f x)
+  "Return the string keybinding for function F and appendage X.
+Values for F include:
+- workspace
+- x11"
+  (prelude/assert (alist/has-key? f kbd/prefixes))
+  (string/format
+   "%s-%s"
+   (alist/get f kbd/prefixes)
+   x))
+
+(defun kbd/for (f x)
+  "Return the `kbd' for function F and appendage X.
+Values for F include:
+- workspace
+- x11"
+  (kbd (kbd/raw f x)))
+
+;; TODO: Prefer copying human-readable versions to the clipboard.  Right now
+;; this isn't too useful.
+(defun kbd/copy-keycode ()
+  "Copy the pressed key to the system clipboard."
+  (interactive)
+  (message "[kbd] Awaiting keypress...")
+  (let ((key (read-key)))
+    (clipboard/copy (string/format "%s" key))
+    (message (string/format "[kbd] \"%s\" copied!" key))))
+
+(defun kbd/print-keycode ()
+  "Prints the pressed keybinding."
+  (interactive)
+  (message "[kbd] Awaiting keypress...")
+  (message (string/format "[kbd] keycode: %s" (read-key))))
+
+;; (when kbd/install-kbds?
+;;   (general-define-key
+;;    :prefix "<SPC>"
+;;    "hr" #'kbd/print-keycode))
+
+(provide 'kbd)
+;;; kbd.el ends here
diff --git a/configs/shared/.emacs.d/wpc/keyboard.el b/configs/shared/.emacs.d/wpc/keyboard.el
new file mode 100644
index 0000000000..f9d13a8e41
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/keyboard.el
@@ -0,0 +1,147 @@
+;;; keyboard.el --- Managing keyboard preferences with Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Setting key repeat and other values.
+;;
+;; Be wary of suspiciously round numbers.  Especially those divisible by ten!
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'string)
+(require 'number)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support clamping functions for repeat-{rate,delay} to ensure only valid
+;; values are sent to xset.
+(defcustom keyboard/repeat-rate 80
+  "The number of key repeat signals sent per second.")
+
+(defcustom keyboard/repeat-delay 170
+  "The number of milliseconds before autorepeat starts.")
+
+(defconst keyboard/repeat-rate-copy keyboard/repeat-rate
+  "Copy of `keyboard/repeat-rate' to support `keyboard/reset-key-repeat'.")
+
+(defconst keyboard/repeat-delay-copy keyboard/repeat-delay
+  "Copy of `keyboard/repeat-delay' to support `keyboard/reset-key-repeat'.")
+
+(defcustom keyboard/install-preferences? t
+  "When t, install keyboard preferences.")
+
+(defcustom keyboard/install-kbds? nil
+  "When t, install keybindings.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun keyboard/message (x)
+  "Message X in a structured way."
+  (message (string/format "[keyboard.el] %s" x)))
+
+(cl-defun keyboard/set-key-repeat (&key
+                                   (rate keyboard/repeat-rate)
+                                   (delay keyboard/repeat-delay))
+  "Use xset to set the key-repeat RATE and DELAY."
+   (shell-command
+    (string/format "xset r rate %s %s" delay rate)))
+
+;; NOTE: Settings like this are machine-dependent. For instance I only need to
+;; do this on my laptop and other devices where I don't have access to my split
+;; keyboard.
+;; NOTE: Running keysym Caps_Lock is not idempotent.  If this is called more
+;; than once, xmodmap will start to error about non-existent Caps_Lock symbol.
+;; For more information see here:
+;; https://unix.stackexchange.com/questions/108207/how-to-map-caps-lock-as-the-compose-key-using-xmodmap-portably-and-idempotently
+(defun keyboard/swap-caps-lock-and-escape ()
+  "Swaps the caps lock and escape keys using xmodmap."
+  (interactive)
+  (shell-command "xmodmap -e 'remove Lock = Caps_Lock'")
+  (shell-command "xmodmap -e 'keysym Caps_Lock = Escape"))
+
+(defun keyboard/inc-repeat-rate ()
+  "Increment `keyboard/repeat-rate'."
+  (interactive)
+  (setq keyboard/repeat-rate (number/inc keyboard/repeat-rate))
+  (keyboard/set-key-repeat :rate keyboard/repeat-rate)
+  (keyboard/message
+   (string/format "Rate: %s" keyboard/repeat-rate)))
+
+(defun keyboard/dec-repeat-rate ()
+  "Decrement `keyboard/repeat-rate'."
+  (interactive)
+  (setq keyboard/repeat-rate (number/dec keyboard/repeat-rate))
+  (keyboard/set-key-repeat :rate keyboard/repeat-rate)
+  (keyboard/message
+   (string/format "Rate: %s" keyboard/repeat-rate)))
+
+(defun keyboard/inc-repeat-delay ()
+  "Increment `keyboard/repeat-delay'."
+  (interactive)
+  (setq keyboard/repeat-delay (number/inc keyboard/repeat-delay))
+  (keyboard/set-key-repeat :delay keyboard/repeat-delay)
+  (keyboard/message
+   (string/format "Delay: %s" keyboard/repeat-delay)))
+
+(defun keyboard/dec-repeat-delay ()
+  "Decrement `keyboard/repeat-delay'."
+  (interactive)
+  (setq keyboard/repeat-delay (number/dec keyboard/repeat-delay))
+  (keyboard/set-key-repeat :delay keyboard/repeat-delay)
+  (keyboard/message
+   (string/format "Delay: %s" keyboard/repeat-delay)))
+
+(defun keyboard/print-key-repeat ()
+  "Print the currently set values for key repeat."
+  (interactive)
+  (keyboard/message
+   (string/format "Rate: %s. Delay: %s"
+                  keyboard/repeat-rate
+                  keyboard/repeat-delay)))
+
+(defun keyboard/set-preferences ()
+  "Reset the keyboard preferences to their default values.
+NOTE: This function exists because occasionally I unplug and re-plug in a
+  keyboard and all of the preferences that I set using xset disappear."
+  (interactive)
+  (keyboard/swap-caps-lock-and-escape)
+  (keyboard/set-key-repeat :rate keyboard/repeat-rate
+                           :delay keyboard/repeat-delay)
+  ;; TODO: Implement this message function as a macro that pulls the current
+  ;; file name.
+  (keyboard/message "Keyboard preferences set!"))
+
+(defun keyboard/reset-key-repeat ()
+  "Set key repeat rate and delay to original values."
+  (interactive)
+  (keyboard/set-key-repeat :rate keyboard/repeat-rate-copy
+                           :delay keyboard/repeat-delay-copy)
+  (keyboard/message "Key repeat preferences reset."))
+
+(when keyboard/install-preferences?
+  (keyboard/set-preferences))
+
+;; TODO: Define minor-mode for this.
+(when keyboard/install-kbds?
+  (general-unbind 'motion "C-i" "C-y")
+  (general-define-key
+   ;; TODO: Choose better KBDs for these that don't interfere with useful evil
+   ;; ones.
+   ;; Use C-y when you accidentally send the key-repeat too high or too low to
+   ;; be meaningful.
+   "C-y" #'keyboard/reset-key-repeat
+   "C-i" #'keyboard/inc-repeat-rate
+   "C-u" #'keyboard/dec-repeat-rate
+   "C-S-i" #'keyboard/inc-repeat-delay
+   "C-S-u" #'keyboard/dec-repeat-delay))
+
+(provide 'keyboard)
+;;; keyboard.el ends here
diff --git a/configs/shared/.emacs.d/wpc/keymap.el b/configs/shared/.emacs.d/wpc/keymap.el
new file mode 100644
index 0000000000..87d340fcdb
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/keymap.el
@@ -0,0 +1,25 @@
+;;; keymap.el --- Working with Elisp keymaps -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Very much a work-in-progress.
+
+;;; Code:
+
+(require 'macros)
+(require 'symbol)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun keymap/pretty-print (x)
+  "Pretty prints `X'."
+  ;; TODO: Work-in-progress
+  (s-concat "\\{" (symbol/to-string x) "}"))
+
+(macros/comment
+ (keymap/pretty-print lispyville-mode-map))
+
+(provide 'keymap)
+;;; keymap.el ends here
diff --git a/configs/shared/.emacs.d/wpc/laptop-battery.el b/configs/shared/.emacs.d/wpc/laptop-battery.el
new file mode 100644
index 0000000000..3ec03553d2
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/laptop-battery.el
@@ -0,0 +1,60 @@
+;;; laptop-battery.el --- Display laptop battery information -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Some wrappers to obtain battery information.
+;;
+;; To troubleshoot battery consumpton look into the CLI `powertop`.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Roadmap
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support functions that work with reporting battery stats.
+;; TODO: low-battery-reporting-threshold
+;; TODO: charged-battery-reporting-threshold
+;; TODO: Format modeline battery information.
+;; TODO: Provide better time information in the modeline.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'battery)
+(require 'alist)
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun laptop-battery/available? ()
+  "Return t if battery information is available."
+  (maybe/some? battery-status-function))
+
+(defun laptop-battery/percentage ()
+  "Return the current percentage of the battery."
+  (->> battery-status-function
+       funcall
+       (alist/get 112)))
+
+(defun laptop-battery/print-percentage ()
+  "Return the current percentage of the battery."
+  (interactive)
+  (->> (laptop-battery/percentage)
+       message))
+
+(defun laptop-battery/display ()
+  "Display laptop battery percentage in the modeline."
+  (interactive)
+  (display-battery-mode 1))
+
+(defun laptop-battery/hide ()
+  "Hide laptop battery percentage in the modeline."
+  (interactive)
+  (display-battery-mode -1))
+
+(provide 'laptop-battery)
+;;; laptop-battery.el ends here
diff --git a/configs/shared/.emacs.d/wpc/list.el b/configs/shared/.emacs.d/wpc/list.el
new file mode 100644
index 0000000000..bc89c13264
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/list.el
@@ -0,0 +1,197 @@
+;;; list.el --- Functions for working with lists. -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Since I prefer having the `list/' namespace, I wrote this module to wrap many
+;; of the functions that are defined in the the global namespace in ELisp.  I
+;; sometimes forget the names of these functions, so it's nice for them to be
+;; organized like this.
+;;
+;; Motivation:
+;; Here are some examples of function names that I cannot tolerate:
+;; - `car': Return the first element (i.e. "head") of a linked list
+;; - `cdr': Return the tail of a linked list
+
+;; As are most APIs for standard libraries that I write, this is heavily
+;; influenced by Elixir's standard library.
+;;
+;; Elixir's List library:
+;; - ++/2
+;; - --/2
+;; - hd/1
+;; - tl/1
+;; - in/2
+;; - length/1
+;;
+;; Similar libraries:
+;; - dash.el: Functional library that mimmicks Clojure.  It is consumed herein.
+;; - list-utils.el: Utility library that covers things that dash.el may not
+;;   cover.
+;;   stream.el: Elisp implementation of streams, "implemented as delayed
+;;   evaluation of cons cells."
+
+;; TODO: Consider naming this file linked-list.el.
+
+;; TODO: Support module-like macro that auto-namespaces functions.
+
+;; TODO: Consider wrapping most data structures like linked-lists,
+;; associative-lists, etc in a `cl-defstruct', so that the dispatching by type
+;; can be nominal instead of duck-typing.  I'm not sure if this is a good idea
+;; or not.  If I do this, I should provide isomorphisms to map between idiomatic
+;; ways of working with Elisp data structures and my wrapped variants.
+
+;; TODO: Are function aliases/synonyms even a good idea?  Or do they just
+;; bloat the API unnecessarily?
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst list/tests? t
+  "When t, run the test suite.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list/new ()
+  "Return a new, empty list."
+  '())
+
+(defun list/concat (&rest lists)
+  "Joins `LISTS' into on list."
+  (apply #'-concat lists))
+
+(defun list/join (joint xs)
+  "Join a list of strings, XS, with JOINT."
+  (if (list/empty? xs)
+      ""
+    (list/reduce (list/first xs)
+                 (lambda (x acc)
+                   (string/concat acc joint x))
+                 (list/tail xs))))
+
+(defun list/length (xs)
+  "Return the number of elements in `XS'."
+  (length xs))
+
+(defun list/get (i xs)
+  "Return the value in `XS' at `I', or nil."
+  (nth i xs))
+
+(defun list/head (xs)
+  "Return the head of `XS'."
+  (car xs))
+
+;; TODO: Learn how to write proper function aliases.
+(defun list/first (xs)
+  "Alias for `list/head' for `XS'."
+  (list/head xs))
+
+(defun list/tail (xs)
+  "Return the tail of `XS'."
+  (cdr xs))
+
+(defun list/reverse (xs)
+  "Reverses `XS'."
+  (reverse xs))
+
+(defun list/cons (x xs)
+  "Add `X' to the head of `XS'."
+  (cons x xs))
+
+;; map, filter, reduce
+
+;; TODO: Create function adapters like swap.
+;; (defun adapter/swap (f)
+;;   "Return a new function that wraps `F' and swaps the arguments."
+;;   (lambda (a b)
+;;     (funcall f b a)))
+
+;; TODO: Make this function work.
+(defun list/reduce (acc f xs)
+  "Return over `XS' calling `F' on an element in `XS'and `ACC'."
+  (-reduce-from (lambda (acc x) (funcall f x acc)) acc xs))
+
+(defun list/map (f xs)
+  "Call `F' on each element of `XS'."
+  (-map f xs))
+
+(defun list/map-indexed (f xs)
+  "Call `F' on each element of `XS' along with its index."
+  (-map-indexed (lambda (i x) (funcall f x i)) xs))
+
+(defun list/filter (p xs)
+  "Return a subset of XS where predicate P returned t."
+  (list/reverse
+   (list/reduce
+    '()
+    (lambda (x acc)
+      (if (funcall p x)
+          (list/cons x acc)
+        acc))
+    xs)))
+
+(defun list/reject (p xs)
+  "Return a subset of XS where predicate of P return nil."
+  (list/filter (lambda (x) (not (funcall p x))) xs))
+
+(defun list/find (p xs)
+  "Return the first x in XS that passes P or nil."
+  (-find p xs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list/instance? (xs)
+  "Return t if `XS' is a list.
+Be leery of using this with things like alists.  Many data structures in Elisp
+  are implemented using linked lists."
+  (listp xs))
+
+(defun list/empty? (xs)
+  "Return t if XS are empty."
+  (= 0 (list/length xs)))
+
+(defun list/all? (p xs)
+  "Return t if all `XS' pass the predicate, `P'."
+  (-all? p xs))
+
+(defun list/any? (p xs)
+  "Return t if any `XS' pass the predicate, `P'."
+  (-any? p xs))
+
+(defun list/contains? (x xs)
+  "Return t if X is in XS using `equal'."
+  (-contains? xs x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(when list/tests?
+  (prelude/assert
+   (= 0
+      (list/length '())))
+  (prelude/assert
+   (= 5
+      (list/length '(1 2 3 4 5))))
+  (prelude/assert
+   (= 16
+      (list/reduce 1 (lambda (x acc) (+ x acc)) '(1 2 3 4 5))))
+  (prelude/assert
+   (equal '(2 4 6 8 10)
+          (list/map (lambda (x) (* x 2)) '(1 2 3 4 5)))))
+
+(provide 'list)
+;;; list.el ends here
diff --git a/configs/shared/.emacs.d/wpc/list.nix b/configs/shared/.emacs.d/wpc/list.nix
new file mode 100644
index 0000000000..e664ba6fd4
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/list.nix
@@ -0,0 +1,8 @@
+{ pkgs ? import (builtins.fetchTarball
+  "https://github.com/tazjin/depot/archive/master.tar.gz") {} }:
+
+pkgs.writeElispBin {
+  name = "list";
+  deps = epkgs: [ epkgs.dash ./prelude.nix ];
+  src = ./list.el;
+}
diff --git a/configs/shared/.emacs.d/wpc/macros.el b/configs/shared/.emacs.d/wpc/macros.el
new file mode 100644
index 0000000000..a41cb8db9a
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/macros.el
@@ -0,0 +1,95 @@
+;;; macros.el --- Helpful variables for making my ELisp life more enjoyable -*- lexical-binding: t -*-
+;; Authpr: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This file contains helpful variables that I use in my ELisp development.
+
+;; TODO: Consider a macro solution for mimmicking OCaml's auto resolution of
+;; dependencies using `load-path' and friends.
+
+;;; Code:
+
+(require 'f)
+(require 'string)
+(require 'symbol)
+
+;; TODO: Support `xi' lambda shorthand macro.
+
+(defmacro enable (mode)
+  "Helper for enabling `MODE'.
+Useful in `add-hook' calls.  Some modes, like `linum-mode' need to be called as
+`(linum-mode 1)', so `(add-hook mode #'linum-mode)' won't work."
+  `#'(lambda nil (,mode 1)))
+
+(defmacro disable (mode)
+  "Helper for disabling `MODE'.
+Useful in `add-hook' calls."
+  `#'(lambda nil (,mode -1)))
+
+(defmacro add-hooks (modes callback)
+  "Add multiple `MODES' for the `CALLBACK'.
+Usage: (add-hooks '(one-mode-hook 'two-mode-hook) #'fn)"
+  `(dolist (mode ,modes)
+     (add-hook mode ,callback)))
+
+(defmacro add-hook-before-save (mode f)
+  "Register a hook, `F', for a mode, `MODE' more conveniently.
+Usage: (add-hook-before-save 'reason-mode-hook #'refmt-before-save)"
+  `(add-hook ,mode
+             (lambda ()
+               (add-hook 'before-save-hook ,f))))
+
+;; TODO: Debug.
+(defmacro macros/ilambda (&rest body)
+  "Surrounds `BODY' with an interactive lambda function."
+  `(lambda ()
+     (interactive)
+     ,@body))
+
+;; TODO: Privatize?
+(defun namespace ()
+  "Return the namespace for a function based on the filename."
+  (->> (buffer-file-name)
+       f-filename
+       f-base))
+
+(defmacro macros/comment (&rest _)
+  "Empty comment s-expresion where `BODY' is ignored."
+  `nil)
+
+;; NOTE: Not prepending the "macros" to this macro, since brevity is its goal.
+(defmacro >> (&rest forms)
+  "Compose a new, point-free function by composing FORMS together."
+  (let ((sym (gensym)))
+    `(lambda (,sym)
+       (->> ,sym ,@forms))))
+
+;; TOOD: Support this.
+(cl-defmacro macros/test
+    (&key function
+          test
+          args
+          expect
+          equality)
+  (let* ((namespace (namespace))
+         (test-name (string/->symbol
+                     (s-concat namespace
+                               "/"
+                               "test"
+                               "/"
+                               (s-chop-prefix
+                                (s-concat namespace "/")
+                                (symbol/to-string function))))))
+    `(ert-deftest ,test-name ()
+       ,test
+       (should (,equality (apply ,function ,args)
+                        ,expect)))))
+
+(defmacro macros/support-file-extension (ext mode)
+  "Register MODE to automatically load with files ending with EXT extension.
+Usage: (macros/support-file-extension \".pb\" protobuf-mode)"
+  (let ((extension (string/format "\\.%s\\'" ext)))
+    `(add-to-list 'auto-mode-alist '(,extension . ,mode))))
+
+(provide 'macros)
+;;; macros.el ends here
diff --git a/configs/shared/.emacs.d/wpc/math.el b/configs/shared/.emacs.d/wpc/math.el
new file mode 100644
index 0000000000..55ddc427c7
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/math.el
@@ -0,0 +1,59 @@
+;;; math.el --- Math stuffs -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Containing some useful mathematical functions.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst math/pi pi
+  "The number pi.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support all three arguments.
+;; Int -> Int -> Int -> Boolean
+(cl-defun math/triangle-of-power (&key base power result)
+  ;; TODO: Assert two of three are set.
+  (cond
+   ((maybe/some? base power result)
+    (error "All three arguments should not be set"))
+   ((maybe/some? power result)
+    (message "power and result"))
+   ((maybe/some? base result)
+    (log result base))
+   ((maybe/some? base power)
+    (expt base power))
+   (t
+    (error "Two of the three arguments must be set"))))
+
+(defun math/mod (x y)
+  "Return X mod Y."
+  (mod x y))
+
+(defun math/exp (x y)
+  "Return X raised to the Y."
+  (expt x y))
+
+(defun math/round (x)
+  "Round X to nearest ones digit."
+  (round x))
+
+(defun math/floor (x)
+  "Floor value X."
+  (floor x))
+
+(provide 'math)
+;;; math.el ends here
diff --git a/configs/shared/.emacs.d/wpc/maybe.el b/configs/shared/.emacs.d/wpc/maybe.el
new file mode 100644
index 0000000000..0973b1ed65
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/maybe.el
@@ -0,0 +1,102 @@
+;;; maybe.el --- Library for dealing with nil values -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Inspired by Elm's Maybe library.
+;;
+;; For now, a Nothing value will be defined exclusively as a nil value.  I'm
+;; uninterested in supported falsiness in this module even at risk of going
+;; against the LISP grain.
+;;
+;; I'm avoiding introducing a struct to handle the creation of Just and Nothing
+;; variants of Maybe.  Perhaps this is a mistake in which case this file would
+;; be more aptly named nil.el.  I may change that.  Because of this limitation,
+;; functions in Elm's Maybe library like andThen, which is the monadic bind for
+;; the Maybe type, doesn't have a home here since we cannot compose multiple
+;; Nothing or Just values without a struct or some other construct.
+;;
+;; Possible names for the variants of a Maybe.
+;; None    | Some
+;; Nothing | Something
+;; None    | Just
+;; Nil     | Set
+;;
+;; NOTE: In Elisp, values like '() (i.e. the empty list) are aliases for nil.
+;; What else in Elisp is an alias in this way?
+;; Examples:
+;; TODO: Provide examples of other nil types in Elisp.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar maybe/test? t
+  "When t, run the test suite defined herein.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun maybe/nil? (x)
+  "Return t if X is nil."
+  (eq nil x))
+
+(defun maybe/some? (x)
+  "Return t when X is non-nil."
+  (not (maybe/nil? x)))
+
+(defun maybe/nils? (&rest xs)
+  "Return t if all XS are nil."
+  (list/all? #'maybe/nil? xs))
+
+(defun maybe/somes? (&rest xs)
+  "Return t if all XS are non-nil."
+  (list/all? #'maybe/some? xs))
+
+(defun maybe/default (default x)
+  "Return DEFAULT when X is nil."
+  (if (maybe/nil? x) default x))
+
+(defun maybe/map (f x)
+  "Apply F to X if X is not nil."
+  (if (maybe/some? x)
+      (funcall f x)
+    x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(when maybe/test?
+  ;; nil?
+  (prelude/assert (maybe/nil? nil))
+  (prelude/refute (maybe/nil? t))
+  ;; some?
+  (prelude/assert (maybe/some? 10))
+  (prelude/refute (maybe/some? nil))
+  ;; nils?
+  (prelude/assert (maybe/nils? nil nil nil nil))
+  (prelude/refute (maybe/nils? nil t nil t))
+  ;; somes?
+  (prelude/assert (maybe/somes? t 10 '(1 2 3) "some"))
+  (prelude/refute (maybe/somes? t nil '(1 2 3) "some"))
+  ;; default
+  (prelude/assert
+   (and (= 0 (maybe/default 5 0))
+        (= 5 (maybe/default 5 nil))))
+  ;; map
+  (prelude/assert
+   (and (= 2 (maybe/map #'1+ 1))
+        (eq nil (maybe/map #'1+ nil)))))
+
+(provide 'maybe)
+;;; maybe.el ends here
diff --git a/configs/shared/.emacs.d/wpc/me-seconds.el b/configs/shared/.emacs.d/wpc/me-seconds.el
new file mode 100644
index 0000000000..f03e5d07d7
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/me-seconds.el
@@ -0,0 +1,245 @@
+;;; me-seconds.el --- How valuable is my time? -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Inspired by Google's concept of SWE-seconds, I decided to try and compute how
+;; value my personal time is.
+;;
+;; This library should integrate with another library that handles currency
+;; conversions using locally cached data for historial values and network
+;; requests for current values.
+;;
+;; Context sensitivity:
+;; Many of the values herein are based on my values that are a function of the
+;; year, my current salary, my current company holiday policy, and my current
+;; country holiday policy.  As such, many of these constants need to be updated
+;; whenever changes occur in order for these functions to be useful.
+;;
+;; Units of time:
+;; - seconds
+;; - minutes
+;; - hours
+;; - days
+;; - weeks
+;; - months
+;; - years
+;;
+;; Wish list:
+;; - I should create a money.el struct to work with herein.  This module would
+;;   expose basic algebra for working with money structs, which would be handy.
+;; - I should create a time.el struct for working with hours in the day.  I'd
+;;   like to be able to do (+ 9:15 17:45) cleanly.
+;;
+;; Terminology:
+;; SWE hours give an order of magnitude approximation to the cost of resources
+;; in dollars per hour at 2115 hours per year.
+;; - SWE hour (SWEh)
+;; - SWE year (SWEy)
+;; - SWE nominal
+;; - SWE opportunity
+;;
+;; Other isomorphisms include:
+;; - Borg GCU
+;; - Borg RAM
+;; - Tape (library)
+;; - Tape (vault)
+;; - Spindles (low latency)
+;; - Spindles (throughput)
+;; - Spindles (throughput)
+;; - Tape (throughput)
+;; - SWE (nominal)
+;; - SWE (opportunity)
+
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'macros)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun me-seconds/salary (amt)
+  "Return the yearly rate of AMT of money in GBP.
+f :: Integer -> Rate"
+  (make-rate :money (make-money :whole amt :fractional 0 :currency 'GBP)
+             :unit 'year))
+
+(defconst me-seconds/salary (me-seconds/salary 80000)
+  "My salary in GBP.")
+
+;; TODO: Consider changing these into units of time.
+(defconst me-seconds/months-per-year 12
+  "Number of months in a year.")
+
+(defconst me-seconds/days-per-year 365
+  "Number of days in a year.")
+
+(defconst me-seconds/hours-per-year (* 24 me-seconds/days-per-year)
+  "Number of hours in a year.")
+
+(defconst me-seconds/minutes-per-year (* 60 me-seconds/hours-per-year)
+  "Number of minutes in a year.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Vacation
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst me-seconds/bank-holidays-per-year 8
+  "Number of bank holidays in the UK each year.")
+
+(defconst me-seconds/pto-days-vacation-per-year 25
+  "Number of days of paid-time-off I receive each year in the UK.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Sleeping
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst me-seconds/sleeping-hours-per-day 8
+  "An approximation of the number of hours I sleep each night on average.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Waking
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst me-seconds/waking-hours-per-day
+  (- 24 me-seconds/sleeping-hours-per-night)
+  "An approximation of the number of hours I sleep each night on average.")
+
+;; TODO: Adjust this for vacation time.
+(defconst me-seconds/waking-hours-per-year
+  (* me-seconds/waking-hours-per-day me-seconds/days-per-year)
+  "The number of hours that I work each year.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Working
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst me-seconds/working-hours-per-day
+  (- 17 9)
+  "An approximation of the number of hours I work each weekday on average.
+Note that this differs from the assumed SWE hours per day calculation, which
+  assumes 9 working hours.  See the discussion about this of go/rules-of-thumb.")
+
+(defconst me-seconds/working-hours-per-year 2115
+  "This number is borrowed from go/rules-of-thumb.")
+
+;; Keep in mind that the following classifications of time:
+;; - 9:00-17:00 M-F. Is this more expensive than time sleeping?
+;; - Weekend
+;; - Weekday
+;; - Working hours
+;; - Waking hours
+;; - Sleeping hours
+;; - Vacation hours
+;;
+;; TODO: Consider tax implications (i.e. after-tax amounts and pre-tax amounts).
+;;
+;; Should these all be treated the same since they all pull from the same pot of
+;; time? Or perhaps there are multiples involved? Much to think about. How does
+;; Google handle this?
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Supported currencies:
+;; - GBP
+;; NOTE: Amount is an integer.
+(cl-defstruct money whole fractional currency)
+(cl-defstruct rate money unit)
+
+;; TODO: Add to money.el.
+(defun money/to-string (x)
+  "Return the string representation of X.
+f :: Money -> String"
+  (let ((currency (money-currency x))
+        (whole (int-to-string (money-whole x)))
+        (fract (int-to-string (money-fractional x))))
+    (pcase currency
+      ('GBP (string/concat "£" whole "." fract))
+      ('USD (string/concat "$" whole "." fract))
+      (_ (error (string/concat
+                 "Currency: \""
+                 (symbol-name currency)
+                 "\" not supported"))))))
+
+(macros/comment
+ (money/to-string
+  (make-money :whole 100 :fractional 99 :currency 'GBP)))
+
+;; TODO: Add to rate.el.
+(defun rate/to-string (x)
+  "Message X as a rate.
+f :: Rate -> String"
+  (string/concat
+   (money/to-string (rate-money x))
+   " / "
+   (pcase (rate-unit x)
+     ('second "sec")
+     ('minute "min")
+     ('hour   "hr")
+     ('day    "day")
+     ('week   "week")
+     ('month  "month")
+     ('year   "year"))))
+
+(macros/comment
+ (rate/to-string
+  (make-rate
+   :money (make-money :whole 10 :fractional 10 :currency 'GBP)
+   :unit 'day)))
+
+;; TODO: Move this to math.el?
+(defun ensure-float (x)
+  "Ensures X is treated as a float."
+  (+ 0.0 x))
+
+;; TODO: Move these to basic time mapping module.
+;; TODO: Consider making this an isomorphism.
+(defun minutes/to-hours (x)
+  "Convert X minutes to n hours."
+  (/ x 60.0))
+
+(defun hours/to-minutes (x)
+  "Convert X hours to n minutes."
+  (* x 60))
+
+(defun days/to-minutes (x)
+  "Convert X days to n minutes."
+  (* x 24 60))
+
+(defun weeks/to-minutes (x)
+  "Convert X weeks to n minutes."
+  (* x 7 24 60))
+
+(defun months/to-minutes (x)
+  "Convert X months to n minutes.
+This approximates the number of days in a month to 30."
+  (* x 30 24 60))
+
+;; TODO: Support algebraic functions with money structs.
+;; TODO: Support isomorphisms for rates to other units of time.  That would
+;; subsume most of this module's use.
+(defun me-seconds/value-per-minute (salary)
+  "Computes my value per minute based on my current SALARY.
+Signature: f :: Rate -> Rate
+This is assuming that all of my time is equally valuable.  See the above
+  discussion about the various classifications of my time.")
+
+;; TODO: See note above about isomorphisms between various rates.
+(defun me-seconds/value (salary x)
+  "Compute the value of X minutes of my time at my current SALARY.
+f :: Rate -> Integer -> Money")
+
+(macros/comment
+ (rate/to-string me-seconds/salary)
+ )
+
+(provide 'me-seconds)
+;;; me-seconds.el ends here
diff --git a/configs/shared/.emacs.d/wpc/monoid.el b/configs/shared/.emacs.d/wpc/monoid.el
new file mode 100644
index 0000000000..401d63c417
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/monoid.el
@@ -0,0 +1,30 @@
+;;; monoid.el --- Working with Monoids in Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; The day has finally arrived where I'm using Monoids in Elisp.
+;;
+;; The monoid typeclass is as follows:
+;; - empty :: a
+;; - concat :: (list a) -> a
+
+;;; Code:
+
+;; TODO: Consider a prelude version that works for all Elisp types.
+(defun monoid/classify (xs)
+  "Return the type of `XS'."
+  (cond
+   ((listp xs) 'list)
+   ((vectorp xs) 'vector)
+   ((stringp xs) 'string)))
+
+
+(defun monoid/empty (xs)
+  "Return the empty monoid for the type `XS'."
+  (pcase (monoid/classify xs)
+    ('list '())
+    ('vector [])
+    ('string "")))
+
+(provide 'monoid)
+;;; monoid.el ends here
diff --git a/configs/shared/.emacs.d/wpc/number.el b/configs/shared/.emacs.d/wpc/number.el
new file mode 100644
index 0000000000..81d3c5d2b9
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/number.el
@@ -0,0 +1,151 @@
+;;; number.el --- Functions for working with numbers -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;;
+;; Classifications of numbers:
+;; - Natural: (a.k.a positive integers, counting numbers); {1, 2, 3, ... }
+;;
+;; - Whole: Natural Numbers, plus zero; {0, 1, 2, 3, ...}
+;;
+;; - Integers: Whole numbers plus all the negatives of the natural numbers;
+;;   {... , -2, -1, 0, 1, 2, ...}
+;;
+;; - Rational numbers: (a.k.a. fractions) where the top and bottom numbers are
+;;   integers; e.g., 1/2, 3/4, 7/2, ⁻4/3, 4/1.  Note: The denominator cannot be
+;;   0, but the numerator can be.
+;;
+;; - Real numbers: All numbers that can be written as a decimal.  This includes
+;;   fractions written in decimal form e.g., 0.5, 0.75 2.35, ⁻0.073, 0.3333, or
+;;   2.142857. It also includes all the irrational numbers such as π, √2 etc.
+;;   Every real number corresponds to a point on the number line.
+;;
+;; The functions defined herein attempt to capture the mathematical definitions
+;; of numbers and their classifications as defined above.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst number/test? t
+  "When t, run the test suite defined herein.")
+
+;; TODO: What about int.el?
+
+;; TODO: How do we handle a number typeclass?
+
+(defun number/positive? (x)
+  "Return t if `X' is a positive number."
+  (> x 0))
+
+(defun number/negative? (x)
+  "Return t if `X' is a positive number."
+  (< x 0))
+
+;; TODO: Don't rely on this. Need to have 10.0 and 10 behave similarly.
+(defun number/float? (x)
+  "Return t if `X' is a floating point number."
+  (floatp x))
+
+(defun number/natural? (x)
+  "Return t if `X' is a natural number."
+  (and (number/positive? x)
+       (not (number/float? x))))
+
+(defun number/whole? (x)
+  "Return t if `X' is a whole number."
+  (or (= 0 x)
+      (number/natural? x)))
+
+(defun number/integer? (x)
+  "Return t if `X' is an integer."
+  (or (number/whole? x)
+      (number/natural? (- x))))
+
+;; TODO: How defensive should these guards be?  Should we assert that the inputs
+;; are integers before checking evenness or oddness?
+
+;; TODO: Look up Runar (from Unison) definition of handling zero as even or odd.
+
+;; TODO: How should rational numbers be handled? Lisp is supposedly famous for
+;; its handling of rational numbers.
+;; TODO: `calc-mode' supports rational numbers as "1:2" meaning "1/2"
+;; (defun number/rational? (x))
+
+;; TODO: Can or should I support real numbers?
+;; (defun number/real? (x))
+
+(defun number/even? (x)
+  "Return t if `X' is an even number."
+  (or (= 0 x)
+      (= 0 (mod x 2))))
+
+(defun number/odd? (x)
+  "Return t if `X' is an odd number."
+  (not (number/even? x)))
+
+(defun number/dec (x)
+  "Subtract one from `X'.
+While this function is undeniably trivial, I have unintentionally done (- 1 x)
+  when in fact I meant to do (- x 1) that I figure it's better for this function
+  to exist, and for me to train myself to reach for it and its inc counterpart."
+  (- x 1))
+
+(defun number/inc (x)
+  "Add one to `X'."
+  (+ x 1))
+
+;; TODO: Does this belong in a math module?  Is math too vague?  Or is number
+;; too vague?
+(defun number/factorial (x)
+  "Return factorial of `X'."
+  (cond
+   ((number/negative? x) (error "Will not take factorial of negative numbers"))
+   ((= 0 x) 1)
+   ;; NOTE: Using `series/range' introduces a circular dependency because:
+   ;; series -> number -> series.  Conceptually, however, this should be
+   ;; perfectly acceptable.
+   (t (->> (series/range 1 x)
+           (list/reduce 1 #'*)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(when number/test?
+  (prelude/assert
+   (number/positive? 10))
+  (prelude/assert
+   (number/natural? 10))
+  (prelude/assert
+   (number/whole? 10))
+  (prelude/assert
+   (number/whole? 0))
+  (prelude/assert
+   (number/integer? 10))
+  (prelude/assert
+   (= 120 (number/factorial 5)))
+  (prelude/assert
+   (number/even? 6))
+  (prelude/refute
+   (number/odd? 6))
+  (prelude/refute
+   (number/positive? -10))
+  (prelude/refute
+   (number/natural? 10.0))
+  (prelude/refute
+   (number/natural? -10))
+  (prelude/refute
+   (number/natural? -10.0)))
+
+(provide 'number)
+;;; number.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-clojure.el b/configs/shared/.emacs.d/wpc/packages/wpc-clojure.el
new file mode 100644
index 0000000000..d9262cdda8
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-clojure.el
@@ -0,0 +1,85 @@
+;;; clojure.el --- My Clojure preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hosting my Clojure tooling preferences
+
+;;; Code:
+
+;; Helper functions
+
+;; (defun wpc/buffer-name-for-clojure-mode (mode)
+;;   (let* ((project-name (projectile-project-name))
+;;          (cljs-name (concat "*cider-repl CLJS " project-name "*"))
+;;          (clj-name  (concat "*cider-repl " project-name "*")))
+;;     (cond ((eq mode 'clojurescript-mode) cljs-name)
+;;           ((eq mode 'clojure-mode) clj-name)
+;;           ((eq mode 'clojurec-mode) cljs-name))))
+
+;; (defun wpc/repl-function-for-clojure-mode (mode)
+;;   (let ((project-name (projectile-project-name))
+;;         (cljs-fn #'cider-jack-in-clojurescript)
+;;         (clj-fn  #'cider-jack-in))
+;;     (cond ((eq mode 'clojurescript-mode) cljs-fn)
+;;           ((eq mode 'clojure-mode) clj-fn)
+;;           ((eq mode 'clojurec-mode) cljs-fn))))
+
+;; (defun wpc/find-or-create-clojure-or-clojurescript-repl ()
+;;   (interactive)
+;;   (with-current-buffer (current-buffer)
+;;     (let ((buffer-name   (wpc/buffer-name-for-clojure-mode major-mode))
+;;           (repl-function (wpc/repl-function-for-clojure-mode major-mode)))
+;;       (if (get-buffer buffer-name)
+;;           (switch-to-buffer buffer-name)
+;;         (funcall repl-function)))))
+
+(use-package clojure-mode
+  :config
+  ;; from Ryan Schmukler:
+  (setq cljr-magic-require-namespaces
+        '(("io" . "clojure.java.io")
+          ("sh" . "clojure.java.shell")
+          ("jdbc" . "clojure.java.jdbc")
+          ("set" . "clojure.set")
+          ("time" . "java-time")
+          ("str" . "cuerdas.core")
+          ("path" . "pathetic.core")
+          ("walk" . "clojure.walk")
+          ("zip" . "clojure.zip")
+          ("async" . "clojure.core.async")
+          ("component" . "com.stuartsierra.component")
+          ("http" . "clj-http.client")
+          ("url" . "cemerick.url")
+          ("sql" . "honeysql.core")
+          ("csv" . "clojure.data.csv")
+          ("json" . "cheshire.core")
+          ("s" . "clojure.spec.alpha")
+          ("fs" . "me.raynes.fs")
+          ("ig" . "integrant.core")
+          ("cp" . "com.climate.claypoole")
+          ("re-frame" . "re-frame.core")
+          ("rf" . "re-frame.core")
+          ("re" . "reagent.core")
+          ("reagent" . "reagent.core")
+          ("u.core" . "utopia.core")
+          ("gen" . "clojure.spec.gen.alpha"))))
+
+(use-package cider
+  :config
+  (general-define-key
+    :keymaps 'cider-repl-mode-map
+    "C-l"    #'cider-repl-clear-buffer
+    "C-u"    #'kill-whole-line
+    "<up>"   #'cider-repl-previous-input
+    "<down>" #'cider-repl-next-input
+    ;; "C-c 'j" #'wpc/find-or-create-clojure-or-clojurescript-repl
+    )
+  ;; (setq cider-cljs-lein-repl
+  ;;       "(do (require 'figwheel-sidecar.repl-api)
+  ;;            (figwheel-sidecar.repl-api/start-figwheel!)
+  ;;            (figwheel-sidecar.repl-api/cljs-repl))"
+  ;;       cider-prompt-for-symbol nil)
+  )
+
+(provide 'wpc-clojure)
+;;; wpc-clojure.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-company.el b/configs/shared/.emacs.d/wpc/packages/wpc-company.el
new file mode 100644
index 0000000000..1152f496c2
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-company.el
@@ -0,0 +1,28 @@
+;;; company.el --- Autocompletion package, company, preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hosts my company mode preferences
+
+;;; Code:
+
+;; autocompletion client
+(use-package company
+  :config
+  (general-define-key
+    :keymaps 'company-active-map
+    "C-j" #'company-select-next
+    "C-n" #'company-select-next
+    "C-k" #'company-select-previous
+    "C-p" #'company-select-previous
+    "C-d" #'company-show-doc-buffer)
+  (setq company-tooltip-align-annotations t)
+  (setq company-idle-delay 0)
+  (setq company-show-numbers t)
+  (setq company-minimum-prefix-length 2)
+  (setq company-dabbrev-downcase nil
+        company-dabbrev-ignore-case t)
+  (global-company-mode))
+
+(provide 'wpc-company)
+;;; wpc-company.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-dired.el b/configs/shared/.emacs.d/wpc/packages/wpc-dired.el
new file mode 100644
index 0000000000..13aed975d2
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-dired.el
@@ -0,0 +1,39 @@
+;;; dired.el --- My dired preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; File management in Emacs, if learned and configured properly, should be
+;; capable to reduce my dependency on the terminal.
+
+;;; Code:
+
+;; TODO: Ensure sorting in dired is by type.
+
+;; TODO: Rename wpc-dired.el to file-management.el
+
+(progn
+  (require 'dired)
+  (setq dired-recursive-copies 'always
+        dired-recursive-deletes 'top
+        dired-dwim-target t)
+  (general-define-key
+   :keymaps 'dired-mode-map
+   :states 'normal
+   "s" nil
+   "q" (lambda () (interactive) (kill-buffer nil))
+   "c" #'find-file
+   "f" #'wpc/find-file
+   "-" (lambda () (interactive) (find-alternate-file "..")))
+  (general-add-hook 'dired-mode-hook
+                    (list (enable dired-hide-details-mode)
+                          #'auto-revert-mode)))
+
+(progn
+  (require 'locate)
+  (general-define-key
+   :keymaps 'locate-mode-map
+   :states 'normal
+   "o" #'dired-display-file))
+
+(provide 'wpc-dired)
+;;; wpc-dired.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-docker.el b/configs/shared/.emacs.d/wpc/packages/wpc-docker.el
new file mode 100644
index 0000000000..270eaec6fe
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-docker.el
@@ -0,0 +1,16 @@
+;;; docker.el --- Docker preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; My Docker preferences and configuration
+
+;;; Code:
+
+(use-package docker
+  :config
+  (setenv "DOCKER_TLS_VERIFY" "1")
+  (setenv "DOCKER_HOST" "tcp://10.11.12.13:2376")
+  (setenv "DOCKER_MACHINE_NAME" "name"))
+
+(provide 'wpc-docker)
+;;; wpc-docker.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-elixir.el b/configs/shared/.emacs.d/wpc/packages/wpc-elixir.el
new file mode 100644
index 0000000000..e64abe70fc
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-elixir.el
@@ -0,0 +1,13 @@
+;;; wpc-elixir.el --- Elixir / Erland configuration -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; My preferences for working with Elixir / Erlang projects
+
+;;; Code:
+(use-package elixir-mode
+  :config
+  (add-hook-before-save 'elixir-mode-hook #'elixir-format))
+
+(provide 'wpc-elixir)
+;;; wpc-elixir.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-flycheck.el b/configs/shared/.emacs.d/wpc/packages/wpc-flycheck.el
new file mode 100644
index 0000000000..d7bb834a62
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-flycheck.el
@@ -0,0 +1,14 @@
+;;; flycheck.el --- My flycheck configuration -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hosts my Flycheck preferences
+
+;;; Code:
+
+(use-package flycheck
+  :config
+  (global-flycheck-mode))
+
+(provide 'wpc-flycheck)
+;;; wpc-flycheck.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-haskell.el b/configs/shared/.emacs.d/wpc/packages/wpc-haskell.el
new file mode 100644
index 0000000000..e8ab16e585
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-haskell.el
@@ -0,0 +1,56 @@
+;;; haskell.el --- My Haskell preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hosts my Haskell development preferences
+
+;;; Code:
+
+;; Haskell support
+
+;; font-locking, glyph support, etc
+(use-package haskell-mode
+  :config
+  (let ((m-symbols
+         '(("`mappend`" . "⊕")
+           ("<>"        . "⊕"))))
+    (dolist (item m-symbols) (add-to-list 'haskell-font-lock-symbols-alist item)))
+  (setq haskell-font-lock-symbols t)
+  (add-hook-before-save 'haskell-mode #'haskell-align-imports))
+
+;; LSP support
+(use-package lsp-haskell
+  :after (haskell-mode)
+  :config
+  (setq lsp-haskell-process-path-hie "hie-wrapper")
+  (add-hook 'haskell-mode-hook #'lsp-haskell-enable)
+  (add-hook 'haskell-mode-hook #'flycheck-mode))
+
+;; Test toggling
+(defun haskell/module->test ()
+  "Jump from a module to a test."
+  (let ((filename (->> buffer-file-name
+                       (s-replace "/src/" "/test/")
+                       (s-replace ".hs" "Test.hs")
+                       find-file)))
+    (make-directory (f-dirname filename) t)
+    (find-file filename)))
+
+(defun haskell/test->module ()
+  "Jump from a test to a module."
+  (let ((filename (->> buffer-file-name
+                       (s-replace "/test/" "/src/")
+                       (s-replace "Test.hs" ".hs")
+                       )))
+    (make-directory (f-dirname filename) t)
+    (find-file filename)))
+
+(defun haskell/test<->module ()
+  "Toggle between test and module in Haskell."
+  (interactive)
+  (if (s-contains? "/src/" buffer-file-name)
+      (haskell/module->test)
+    (haskell/test->module)))
+
+(provide 'wpc-haskell)
+;;; wpc-haskell.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-java.el b/configs/shared/.emacs.d/wpc/packages/wpc-java.el
new file mode 100644
index 0000000000..4f33ba962e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-java.el
@@ -0,0 +1,42 @@
+;;; wpc-java.el --- Java configuration -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; When life gets you down, and you find yourself writing Java, remember: at
+;; least you're using Emacs.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'macros)
+
+(prelude/assert
+ (prelude/executable-exists? "google-java-format"))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Troubleshoot why this isn't running.
+(add-hook-before-save
+ 'java-mode-hook
+ (lambda ()
+   (call-interactively
+    #'google-java-format)))
+
+(add-hook 'java-mode-hook
+          (lambda ()
+            (setq c-basic-offset 2
+                  tab-width 2)))
+
+;; TODO: Figure out whether I should use this or google-emacs.
+;; (use-package lsp-java
+;;   :config
+;;   (add-hook 'java-mode-hook #'lsp))
+
+(provide 'wpc-java)
+;;; wpc-java.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-javascript.el b/configs/shared/.emacs.d/wpc/packages/wpc-javascript.el
new file mode 100644
index 0000000000..b55e03e00c
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-javascript.el
@@ -0,0 +1,61 @@
+;; wpc-javascript.el --- My Javascript preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This module hosts my Javascript tooling preferences
+;;
+;; Depends
+;; - yarn global add prettier
+
+;;; Code:
+
+;; Constants
+(defconst wpc/js-hooks
+  '(js-mode-hook js2-mode-hook rjsx-mode-hook)
+  "All of the commonly used hooks for Javascript buffers.")
+
+(defconst wpc/frontend-hooks
+  (-insert-at 0 'css-mode-hook wpc/js-hooks)
+  "All of the commonly user hooks for frontend development.")
+
+
+;; frontend indentation settings
+(setq js-indent-level 2
+      css-indent-offset 2)
+
+;; ;; javascript
+;; (evil-leader/set-key-for-mode 'rjsx-mode "t" #'wpc/toggle-between-js-test-and-module)
+;; (evil-leader/set-key-for-mode 'rjsx-mode "x" #'wpc/toggle-between-js-component-and-store)
+;; (evil-leader/set-key-for-mode 'rjsx-mode "u" #'wpc/jump-to-parent-file)
+
+;; Flow for Javascript
+(use-package add-node-modules-path
+  :config
+  (general-add-hook wpc/js-hooks #'add-node-modules-path))
+
+(use-package web-mode
+  :mode "\\.html\\'"
+  :config
+  (setq web-mode-css-indent-offset 2)
+  (setq web-mode-code-indent-offset 2)
+  (setq web-mode-markup-indent-offset 2))
+
+;; JSX highlighting
+(use-package rjsx-mode
+  :mode "\\.js\\'"
+  :config
+  (general-unbind rjsx-mode-map "<" ">" "C-d")
+  (general-nmap
+    :keymaps 'rjsx-mode-map
+    "K" #'flow-minor-type-at-pos)
+  (setq js2-mode-show-parse-errors nil
+        js2-mode-show-strict-warnings nil))
+
+;; JS autoformatting
+(use-package prettier-js
+  :after (rjsx-mode)
+  :config
+  (general-add-hook wpc/frontend-hooks #'prettier-js-mode))
+
+(provide 'wpc-javascript)
+;;; wpc-javascript.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-keybindings.el b/configs/shared/.emacs.d/wpc/packages/wpc-keybindings.el
new file mode 100644
index 0000000000..fb9c78f0e7
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-keybindings.el
@@ -0,0 +1,206 @@
+;;; keybindings.el --- My Evil preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This module hosts my Evil preferences
+;;
+;; Wish List:
+;; - drop support for `evil-leader' library in favor of `general.el'
+;; - restore support for concise (n <kbd> <function>) instead of `general-mmap'
+;; - restore support for `general-unbind'
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Packages
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; This may be contraversial, but I never use the prefix key, and I'd prefer to
+;; have to bound to the readline function that deletes the entire line.
+(general-unbind "C-u")
+
+(use-package evil
+  :init
+  ;; Should remove the warning messages on init.
+  (setq evil-want-integration t)
+  ;; TODO: Troubleshoot why this binding causes the following warning:
+  ;; "Warning (evil-collection): `evil-want-keybinding' was set to nil but not
+  ;; before loading evil."
+  (setq evil-want-keybinding nil)
+  (general-evil-setup)
+  :config
+  ;; Ensure that evil's command mode behaves with readline bindings.
+  (general-define-key
+   :keymaps 'evil-ex-completion-map
+   "C-a" #'move-beginning-of-line
+   "C-e" #'move-end-of-line
+   "C-k" #'kill-line
+   "C-u" #'evil-delete-whole-line
+   "C-v" #'evil-paste-after
+   "C-d" #'delete-char
+   "C-f" #'forward-char
+   "M-b" #'backward-word
+   "M-f" #'forward-word
+   "M-d" #'kill-word
+   "M-DEL" #'backward-kill-word
+   "C-b" #'backward-char)
+  ;; TODO: Ensure all of my custom keybindings end up in a single map that is
+  ;; easy to enable or disable.
+  (general-mmap
+    :keymaps 'override
+    "RET" #'evil-goto-line
+    "H"   #'evil-first-non-blank
+    "L"   #'evil-end-of-line
+    "_"   #'ranger
+    "-"   #'dired-jump
+    "sl"  #'wpc/evil-window-vsplit-right
+    "sh"  #'evil-window-vsplit
+    "sk"  #'evil-window-split
+    "sj"  #'wpc/evil-window-split-down)
+  (general-nmap
+    :keymaps 'override
+    "gd" #'xref-find-definitions
+    ;; Wrapping `xref-find-references' in the `let' binding to prevent xref from
+    ;; prompting.  There are other ways to handle this variable, such as setting
+    ;; it globally with `setq' or buffer-locally with `setq-local'.  For now, I
+    ;; prefer setting it with `let', which should bind it in the dynamic scope
+    ;; for the duration of the `xref-find-references' function call.
+    "gx" (lambda ()
+           (interactive)
+           (let ((xref-prompt-for-identifier nil))
+             (call-interactively #'xref-find-references))))
+  (general-unbind 'motion "M-." "C-p")
+  (general-unbind 'normal "s"   "M-." "C-p" "C-n")
+  (general-unbind 'insert "C-v" "C-d" "C-a" "C-e" "C-n" "C-p" "C-k")
+  (setq evil-symbol-word-search t)
+  (evil-mode 1))
+
+;; evil keybindings
+(use-package evil-collection
+  :after (evil)
+  :config
+  (evil-collection-init))
+
+;; expose a leader key
+(use-package evil-leader
+  :after (evil)
+  :config
+  (global-evil-leader-mode 1)
+  (evil-leader/set-leader "<SPC>")
+  (evil-leader/set-key
+    "i"   #'counsel-semantic-or-imenu
+    "I"   #'ibuffer
+    "hk"  #'helpful-callable
+    "hf"  #'helpful-function
+    "hm"  #'helpful-macro
+    "hc"  #'helpful-command
+    "hk"  #'helpful-key
+    "hv"  #'helpful-variable
+    "hp"  #'helpful-at-point
+    "s"   #'flyspell-mode
+    "S"   #'sort-lines
+    "a"   #'wpc-terminal/toggle
+    "="   #'align
+    "p"   #'flycheck-previous-error
+    "f"   #'wpc/find-file
+    "n"   #'flycheck-next-error
+    "N"   #'smerge-next
+    "b"   #'ivy-switch-buffer
+    "W"   #'balance-windows
+    "gs"  #'magit-status
+
+    "es" #'wpc/create-snippet
+    ;; TODO: Replace with `macros/ilambda' when that is working again.
+    "ev" (lambda () (interactive) (wpc/find-file-split "~/.config/nvim/init.vim"))
+    "ee" (lambda () (interactive) (wpc/find-file-split "~/.emacs.d/init.el"))
+    "ez" (lambda () (interactive) (wpc/find-file-split "~/.zshrc"))
+    "ea" (lambda () (interactive) (wpc/find-file-split "~/aliases.zsh"))
+    "ef" (lambda () (interactive) (wpc/find-file-split "~/functions.zsh"))
+    "el" (lambda () (interactive) (wpc/find-file-split "~/variables.zsh"))
+    "ex" (lambda () (interactive) (wpc/find-file-split "~/.Xresources"))
+    "ei" (lambda () (interactive) (wpc/find-file-split "~/.config/i3/config.shared"))
+    "em" (lambda () (interactive) (wpc/find-file-split "~/.tmux.conf"))
+
+    "l"  #'locate
+    "L"  #'list-packages
+    "B"  #'magit-blame
+    "w"  #'save-buffer
+    "r"  #'wpc/evil-replace-under-point
+    "R"  #'deadgrep))
+
+;; create comments easily
+(use-package evil-commentary
+  :after (evil)
+  :config
+  (evil-commentary-mode))
+
+;; evil surround
+(use-package evil-surround
+  :after (evil)
+  :config
+  (global-evil-surround-mode 1))
+
+;; I expect in insert mode:
+;; C-a: beginning-of-line
+;; C-e: end-of-line
+;; C-b: backwards-char
+;; C-f: forwards-char
+
+;; TODO: Move these KBD constants to kbd.el.
+
+(defconst wpc/up-kbds
+  '("C-p" "C-k" "<backtab>" "<up>")
+  "The keybindings that I expect to work for moving upwards in lists.")
+
+(defconst wpc/down-kbds
+  '("C-n" "C-j" "<tab>" "<down>")
+  "The keybindings that I expect to work for moving downwards in lists.")
+
+(defconst wpc/left-kbds
+  '("C-b" "<left>")
+  "The keybindings that I expect to move leftwards in insert-like modes.")
+
+(defconst wpc/right-kbds
+  '("C-f" "<right>")
+  "The keybindings that I expect to move rightwards in insert-like modes.")
+
+(defun wpc/ensure-kbds (_ignore)
+  "Try to ensure that my keybindings retain priority over other minor modes."
+  (unless (eq (caar minor-mode-map-alist) 'wpc/kbds-minor-mode)
+    (let ((mykbds (assq 'wpc/kbds-minor-mode minor-mode-map-alist)))
+      (assq-delete-all 'wpc/kbds-minor-mode minor-mode-map-alist)
+      (add-to-list 'minor-mode-map-alist mykbds))))
+
+;; Custom minor mode that ensures that my kbds are available no matter which
+;; major or minor modes are active.
+(add-hook 'after-load-functions #'wpc/ensure-kbds)
+
+;; TODO: Prefer using general and 'override maps to implement this.
+(defvar wpc/kbds
+  (let ((map (make-sparse-keymap)))
+    (bind-keys :map map
+               ("M-q"            . delete-window)
+               ("<s-return>"     . toggle-frame-fullscreen)
+               ("M-h"            . windmove-left)
+               ("M-l"            . windmove-right)
+               ("M-k"            . windmove-up)
+               ("M-j"            . windmove-down)
+               ("M-q"            . delete-window))
+    map)
+  "William Carroll's keybindings that should have the highest precedence.")
+
+(define-minor-mode wpc/kbds-minor-mode
+  "A minor mode so that my key settings override annoying major modes."
+  :init-value t
+  :lighter " wpc/kbds"
+  :keymap wpc/kbds)
+
+;; allow jk to escape
+(use-package key-chord
+  :after (evil)
+  :config
+  (key-chord-mode 1)
+  (key-chord-define evil-insert-state-map "jk" 'evil-normal-state))
+
+(provide 'wpc-keybindings)
+;;; wpc-keybindings.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-lisp.el b/configs/shared/.emacs.d/wpc/packages/wpc-lisp.el
new file mode 100644
index 0000000000..553dff1acb
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-lisp.el
@@ -0,0 +1,114 @@
+;;; lisp.el --- Generic LISP preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;; parent (up)
+;; child (down)
+;; prev-sibling (left)
+;; next-sibling (right)
+
+;;; Code:
+
+(defconst wpc/lisp-mode-hooks
+  '(lisp-mode-hook
+    emacs-lisp-mode-hook
+    clojure-mode-hook
+    clojurescript-mode-hook
+    racket-mode-hook)
+  "List of LISP modes.")
+
+(use-package racket-mode
+  :config
+  (general-define-key
+   :keymaps 'racket-mode-map
+   :states 'normal
+   :prefix "<SPC>"
+   "x" #'racket-send-definition)
+  (general-define-key
+   :keymaps 'racket-mode-map
+   :states 'normal
+   :prefix "<SPC>"
+   "X" #'racket-run)
+  (general-define-key
+   :keymaps 'racket-mode-map
+   :states 'normal
+   :prefix "<SPC>"
+   "d" #'racket-describe)
+  (setq racket-program "~/.nix-profile/bin/racket"))
+
+(use-package lispyville
+  :init
+  (defconst lispyville-key-themes
+    '(c-w
+      operators
+      text-objects
+      ;; Disabling this because I don't enjoy the way it moves around comments.
+      ;; atom-motions
+      prettify
+      commentary
+      slurp/barf-cp
+      wrap
+      additional
+      additional-insert
+      additional-wrap
+      escape)
+    "All available key-themes in Lispyville.")
+  :config
+  (general-add-hook wpc/lisp-mode-hooks #'lispyville-mode)
+  (lispyville-set-key-theme lispyville-key-themes)
+  (progn
+    ;;
+    (general-define-key
+     :keymaps 'lispyville-mode-map
+     :states 'motion
+     ;; first unbind
+     "M-h" nil
+     "M-l" nil)
+    (general-define-key
+     :keymaps 'lispyville-mode-map
+     :states 'normal
+     ;; first unbind
+     "M-j" nil
+     "M-k" nil
+     ;; second rebind
+     ;; TODO: Rebind to something that doesn't conflict with window resizing.
+     ;; "C-M-h" #'lispyville-drag-backward
+     ;; "C-M-l" #'lispyville-drag-forward
+     )))
+
+;; deletes all bindings of f->kbd
+;; binds kbd->
+;; (kbd/bind-function->key
+;;  :keymap 'lispyville-mode-map
+;;  :states 'motion
+;;  #'lispyville-drag-backward "H")
+
+;; Elisp
+(use-package elisp-slime-nav
+  :config
+  (general-add-hook 'emacs-lisp-mode #'ielm-mode))
+
+;; TODO: Should I be using `general-define-key' or `evil-leader/set-key'?  My
+;; gut say `general-define-key'.
+(general-define-key
+ :keymaps 'emacs-lisp-mode-map
+ :states 'normal
+ :prefix "<SPC>"
+ "x" #'eval-defun)
+
+(general-define-key
+ :keymaps 'emacs-lisp-mode-map
+ :states 'normal
+ :prefix "<SPC>"
+ "X" #'eval-buffer)
+
+(general-define-key
+ :keymaps 'emacs-lisp-mode-map
+ :states 'normal
+ :prefix "<SPC>"
+ "d" (lambda ()
+       (interactive)
+       (with-current-buffer (current-buffer)
+         (helpful-function (symbol-at-point)))))
+
+(provide 'wpc-lisp)
+;;; wpc-lisp.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-misc.el b/configs/shared/.emacs.d/wpc/packages/wpc-misc.el
new file mode 100644
index 0000000000..3ddfe0b3e4
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-misc.el
@@ -0,0 +1,209 @@
+;;; misc.el --- Hosting miscellaneous configuration -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This is the home of any configuration that couldn't find a better home.
+
+;;; Code:
+
+;; Display time in the modeline
+;; TODO: Save preferred date format strings and cycle through them since I waver
+;; about which is my favorite.
+(setq display-time-format "%R %a %d %b [%U of 52 weeks]")
+(display-time-mode 1)
+
+;; disable custom variable entries from being written to ~/.emacs.d/init.el
+(setq custom-file "~/.emacs.d/custom.el")
+(load custom-file 'noerror)
+
+;; integrate Emacs with X11 clipboard
+(setq select-enable-primary t)
+(setq select-enable-clipboard t)
+(general-def 'insert
+  "s-v" #'clipboard-yank
+  "C-S-v" #'clipboard-yank)
+
+;; transparently edit compressed files
+(auto-compression-mode t)
+
+;; autowrap when over the fill-column
+(setq-default auto-fill-function #'do-auto-fill)
+
+;; link to Emacs source code
+;; TODO: Update this link.
+(setq find-function-C-source-directory
+      "~/Dropbox/programming/emacs/src")
+
+;; change emacs prompts from "yes or no" -> "y or n"
+(fset 'yes-or-no-p 'y-or-n-p)
+
+;; open photos in Emacs
+(auto-image-file-mode 1)
+
+;; disable line-wrapping
+(setq-default truncate-lines 1)
+
+;; shell file indentation
+(setq sh-basic-offset 2)
+(setq sh-indentation 2)
+
+;; disable company mode when editing markdown
+;; TODO: move this out of wpc-misc.el and into a later file to call
+;; `(disable company-mode)'
+(use-package markdown-mode
+  :config
+  ;; TODO: Add assertion that pandoc is installed and it is accessible from
+  ;; Emacs.
+  (setq markdown-command "pandoc")
+  (setq markdown-split-window-direction 'right)
+  (add-hook 'markdown-mode-hook #'markdown-live-preview-mode))
+
+;; Required by some google-emacs package commands.
+(use-package deferred)
+
+;; git integration
+(use-package magit)
+
+;; http
+(use-package request)
+
+;; perl-compatible regular expressions
+(use-package pcre2el)
+
+;; alternative to help
+(use-package helpful)
+
+;; persist history etc b/w Emacs sessions
+(setq desktop-save 'if-exists)
+(desktop-save-mode 1)
+(setq desktop-globals-to-save
+      (append '((extended-command-history . 30)
+                (file-name-history        . 100)
+                (grep-history             . 30)
+                (compile-history          . 30)
+                (minibuffer-history       . 50)
+                (query-replace-history    . 60)
+                (read-expression-history  . 60)
+                (regexp-history           . 60)
+                (regexp-search-ring       . 20)
+                (search-ring              . 20)
+                (shell-command-history    . 50)
+                tags-file-name
+                register-alist)))
+
+;; config Emacs to use $PATH values
+(use-package exec-path-from-shell
+  :if (memq window-system '(mac ns))
+  :config
+  (exec-path-from-shell-initialize))
+
+;; Emacs autosave, backup, interlocking files
+(setq auto-save-default nil
+      make-backup-files nil
+      create-lockfiles nil)
+
+;; ensure code wraps at 80 characters by default
+(setq-default fill-column constants/fill-column)
+
+(put 'narrow-to-region 'disabled nil)
+
+;; trim whitespace on save
+(add-hook 'before-save-hook #'delete-trailing-whitespace)
+
+;; use tabs instead of spaces
+(setq-default indent-tabs-mode nil)
+
+;; automatically follow symlinks
+(setq vc-follow-symlinks t)
+
+;; fullscreen settings
+(defvar ns-use-native-fullscreen nil)
+
+;; auto-close parens, brackets, quotes
+(electric-pair-mode 1)
+
+(use-package yasnippet
+  :config
+  (yas-global-mode 1))
+
+(use-package projectile
+  :config
+  (projectile-mode t))
+
+(use-package deadgrep
+  :config
+  (general-define-key
+   :keymaps 'deadgrep-mode-map
+   :states 'normal
+   "o" #'deadgrep-visit-result-other-window)
+  (advice-add
+   'deadgrep--format-command
+   :filter-return
+   (lambda (cmd)
+     (replace-regexp-in-string
+      "^rg " "rg --hidden " cmd))))
+
+;; TODO: Do I need this when I have swiper?
+(use-package counsel)
+
+(use-package counsel-projectile)
+
+;; search Google, Stackoverflow from within Emacs
+(use-package engine-mode
+  :config
+  (defengine google
+    "http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s"
+    :keybinding "g")
+  (defengine stack-overflow
+    "https://stackoverflow.com/search?q=%s"
+    :keybinding "s"))
+
+;; EGlot (another LSP client)
+(use-package eglot)
+
+;; Microsoft's Debug Adapter Protocol (DAP)
+(use-package dap-mode
+  :after lsp-mode
+  :config
+  (dap-mode 1)
+  (dap-ui-mode 1))
+
+;; Microsoft's Language Server Protocol (LSP)
+(use-package lsp-ui
+  :config
+  (add-hook 'lsp-mode-hook #'lsp-ui-mode))
+
+(use-package company-lsp
+  :config
+  (push 'company-lsp company-backends))
+
+;; Wilfred/suggest.el - Tool for discovering functions basesd on declaring your
+;; desired inputs and outputs.
+(use-package suggest)
+
+;; Malabarba/paradox - Enhances the `list-packages' view.
+(use-package paradox
+  :config
+  (paradox-enable))
+
+;; TODO: Consider supporting a wpc-elisp.el package for Elisp tooling.
+;; The following functions are quite useful for Elisp development:
+;; - `emr-el-find-unused-definitions'
+(use-package emr
+  :config
+  (define-key prog-mode-map (kbd "M-RET") #'emr-show-refactor-menu))
+
+(defun wpc/frame-name ()
+  "Return the name of the current frame."
+  (frame-parameter nil 'name))
+
+;; Even if I resolved the socket-name resolution issue, I couldn't find an
+;; elegant way to reuse GUI frames. GUIs for me have the advantage of supporting
+;; True Color, support additional keys for KBDs (i.e. super), and aren't limited
+;; by the terminal for rendering certain things.
+(require 'server)
+(when (not (server-running-p))
+  (server-start))
+
+(provide 'wpc-misc)
+;;; wpc-misc.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-nix.el b/configs/shared/.emacs.d/wpc/packages/wpc-nix.el
new file mode 100644
index 0000000000..af439c442d
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-nix.el
@@ -0,0 +1,12 @@
+;;; wpc-nix.el --- Nix support -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Configuration to support working with Nix.
+
+;;; Code:
+(use-package nix-mode
+  :mode "\\.nix\\'")
+
+(provide 'wpc-nix)
+;;; wpc-nix.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-ocaml.el b/configs/shared/.emacs.d/wpc/packages/wpc-ocaml.el
new file mode 100644
index 0000000000..3b898d32be
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-ocaml.el
@@ -0,0 +1,51 @@
+;;; wpc-ocaml.el --- My OCaml preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Tooling support for OCaml development.
+;;
+;; Dependencies:
+;; - `opam install tuareg`
+;; - `opam install merlin`
+;; - `opam install user-setup`
+;; - `opam install ocamlformat`
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'f)
+
+(prelude/assert
+ (prelude/executable-exists? "opam"))
+
+(defvar opam-installs "~/.opam/4.08.0/share/emacs/site-lisp"
+  "Path to the Ocaml PAckage Manager installations.")
+
+(defvar opam-user-setup "~/.emacs.d/opam-user-setup.el"
+  "File for the OPAM Emacs integration.")
+
+(prelude/assert
+ (f-file? opam-user-setup))
+
+(prelude/assert
+ (f-dir? opam-installs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package tuareg
+  :config
+  (add-hook-before-save 'tuareg-mode-hook #'ocamlformat-before-save))
+
+;; ocamlformat
+(require 'opam-user-setup "~/.emacs.d/opam-user-setup.el")
+(require 'ocamlformat)
+(add-to-list 'load-path opam-installs)
+
+(provide 'wpc-ocaml)
+;;; wpc-ocaml.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-org.el b/configs/shared/.emacs.d/wpc/packages/wpc-org.el
new file mode 100644
index 0000000000..d1d981a3ea
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-org.el
@@ -0,0 +1,78 @@
+;;; org.el --- My org preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hosts my org mode preferences
+
+;;; Code:
+
+;; TODO: figure out how to nest this in (use-package org ...)
+(setq org-capture-templates
+      `(
+
+        ("w" "work" entry (file+headline
+                           ,(f-join (getenv "ORG_DIRECTORY") "work.org")
+                           "Tasks")
+         "* TODO %?")
+
+        ("p" "personal" entry (file+headline
+                               ,(f-join (getenv "ORG_DIRECTORY") "personal.org")
+                               "Tasks")
+         "* TODO %? ")
+
+        ("i" "ideas" entry (file+headline
+                            ,(f-join (getenv "ORG_DIRECTORY") "ideas.org")
+                            "Tasks")
+         "* %? ")
+
+        ("s" "shopping list" entry (file+headline
+                            ,(f-join (getenv "ORG_DIRECTORY") "shopping.org")
+                            "Items")
+         "* TODO %? ")
+
+        ))
+(evil-set-initial-state 'org-mode 'normal)
+
+(use-package org
+  :config
+  (general-add-hook 'org-mode-hook
+                    ;; TODO: consider supporting `(disable (list linum-mode company-mode))'
+                    (list (disable linum-mode)
+                          (disable company-mode)))
+  (general-define-key :prefix "C-c"
+           "l" #'org-store-link
+           "a" #'org-agenda
+           "c" #'org-capture)
+  (setq org-startup-folded nil)
+  (setq org-todo-keywords
+        '((sequence "TODO" "BLOCKED" "DONE")))
+  (setq org-default-notes-file (f-join (getenv "ORG_DIRECTORY") "notes.org"))
+  (setq org-agenda-files (list (f-join (getenv "ORG_DIRECTORY") "work.org")
+                               (f-join (getenv "ORG_DIRECTORY") "personal.org")))
+  ;; TODO: troubleshoot why `wpc/kbds-minor-mode', `wpc/ensure-kbds' aren't
+  ;; enough to override the following KBDs. See this discussion for more context
+  ;; on where the idea came from:
+  ;; https://stackoverflow.com/questions/683425/globally-override-key-binding-in-emacs
+  (general-unbind 'normal org-mode-map "M-h" "M-j" "M-k" "M-l"))
+
+(use-package org-bullets
+  :after (org)
+  :config
+  (general-add-hook 'org-mode-hook (enable org-bullets-mode)))
+
+;; i3, `org-mode' integration
+;; Heavily influenced by: https://somethingsomething.us/post/i3_and_orgmode/
+;; TODO: Consider generalizing this since we're using "floating".
+(defadvice org-switch-to-buffer-other-window
+    (after supress-window-splitting activate)
+  "Delete the extra window if we're in a capture frame."
+  (if (equal "floating" (wpc/frame-name))
+      (delete-other-windows)))
+
+(add-hook 'org-capture-after-finalize-hook
+          (lambda ()
+            (when (equal "floating" (wpc/frame-name))
+                (delete-frame))))
+
+(provide 'wpc-org)
+;;; wpc-org.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-package.el b/configs/shared/.emacs.d/wpc/packages/wpc-package.el
new file mode 100644
index 0000000000..6f43330ecb
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-package.el
@@ -0,0 +1,27 @@
+;;; package.el --- My package configuration -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This module hosts all of the settings required to work with ELPA,
+;; MELPA, QUELPA, and co.
+
+;;; Code:
+
+(require 'package)
+(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
+(package-initialize)
+
+(unless (package-installed-p 'use-package)
+  (package-refresh-contents)
+  (package-install 'use-package))
+(eval-when-compile
+  (require 'use-package))
+(setq use-package-always-ensure t)
+(use-package general)
+
+(add-to-list 'load-path "~/.emacs.d/vendor/")
+(add-to-list 'load-path "~/.emacs.d/wpc/")
+(add-to-list 'load-path "~/.emacs.d/wpc/packages")
+
+(provide 'wpc-package)
+;;; wpc-package.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-python.el b/configs/shared/.emacs.d/wpc/packages/wpc-python.el
new file mode 100644
index 0000000000..d0c6dd0d3e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-python.el
@@ -0,0 +1,17 @@
+;;; wpc-python.el --- Python configuration -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; My Python configuration settings
+;;
+;; Depends
+;; - `apti yapf`
+
+;;; Code:
+
+(use-package py-yapf
+  :config
+  (add-hook 'python-mode-hook #'py-yapf-enable-on-save))
+
+(provide 'wpc-python)
+;;; wpc-python.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-reasonml.el b/configs/shared/.emacs.d/wpc/packages/wpc-reasonml.el
new file mode 100644
index 0000000000..909c33d121
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-reasonml.el
@@ -0,0 +1,29 @@
+;;; wpc-reasonml.el --- My ReasonML preferences -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Tooling support for ReasonML development.
+;;
+;; Dependencies:
+;; - `opam install tuareg`
+;; - `opam install merlin`
+;; - `opam install user-setup`
+;; - `opam install ocamlformat`
+
+;;; Code:
+
+;; ReasonML configuration
+(use-package reason-mode
+  :config
+  (add-hook-before-save 'reason-mode-hook #'refmt-before-save))
+
+;; ReasonML LSP configuration
+(lsp-register-client
+ (make-lsp-client :new-connection (lsp-stdio-connection (f-full "~/programming/dependencies/reason-language-server"))
+                  :major-modes '(reason-mode)
+                  :notification-handlers (ht ("client/registerCapability" 'ignore))
+                  :priority 1
+                  :server-id 'reason-ls))
+
+(provide 'wpc-reasonml)
+;;; wpc-reasonml.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-rust.el b/configs/shared/.emacs.d/wpc/packages/wpc-rust.el
new file mode 100644
index 0000000000..fafa27d18c
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-rust.el
@@ -0,0 +1,34 @@
+;;; wpc-rust.el --- Support Rust language -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Supports my Rust work.
+;;
+;; Dependencies:
+;; - `rustup`
+;; - `rustup component add rust-src`
+;; - `rustup toolchain add nightly && cargo +nightly install racer`
+
+
+;;; Code:
+(use-package racer
+  :config
+  (setq rust-sysroot (->> "~/.cargo/bin/rustc --print sysroot"
+                          shell-command-to-string
+                          s-trim-right))
+  (setq racer-rust-src-path (f-join rust-sysroot "lib/rustlib/src/rust/src"))
+  (add-hook 'racer-mode-hook #'eldoc-mode))
+
+(use-package rust-mode
+  :config
+  (add-hook 'rust-mode-hook #'racer-mode)
+  (add-hook-before-save 'rust-mode-hook #'rust-format-buffer)
+  (define-key rust-mode-map
+    (kbd "TAB")
+    #'company-indent-or-complete-common)
+  (define-key rust-mode-map
+    (kbd "M-d")
+    #'racer-describe))
+
+(provide 'wpc-rust)
+;;; wpc-rust.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-shell.el b/configs/shared/.emacs.d/wpc/packages/wpc-shell.el
new file mode 100644
index 0000000000..14d0489030
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-shell.el
@@ -0,0 +1,15 @@
+;;; wpc-shell.el --- POSIX Shell scripting support -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Helpers for my shell scripting. Includes bash, zsh, etc.
+
+;;; Code:
+
+(use-package flymake-shellcheck
+  :commands flymake-shellcheck-load
+  :init
+  (add-hook 'sh-mode-hook #'flymake-shellcheck-load))
+
+(provide 'wpc-shell)
+;;; wpc-shell.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-terminal.el b/configs/shared/.emacs.d/wpc/packages/wpc-terminal.el
new file mode 100644
index 0000000000..c232bb85a7
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-terminal.el
@@ -0,0 +1,70 @@
+;;; terminal.el --- My cobbled together terminal -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; My attempts at creating a sane Emacs terminal.  Most of this work was created
+;; before I discovered and fully adopted EXWM.  Prior to this, the appeal of
+;; having terminals inside of Emacs was appealing.  So appealing in fact that I
+;; was willing to work with inferior alternatives to non-Emacs terminals
+;; (e.g. `ansi-term') instead of GUI alternatives like `alacritty` because the
+;; productivity gains of having a terminal inside of Emacs might outweigh the
+;; shortcomings of that particular terminal.
+;;
+;; All of this changed, however, after discovering EXWM, since I can embed X11
+;; GUI windows inside of Emacs.  Therefore, most of this module is maintained
+;; for historical purposes.
+;;
+;; Benefits of `ansi-term':
+;; - Color scheme remains consistent between Emacs and terminal.
+;; - Same applies to my fonts.
+;;
+;; Downsides of `ansi-term':
+;; - Paging feels sluggish with programs like `cat` and `less`.
+;; - KBDs don't provide 100% coverage of what I expect from a terminal since
+;;   they were created to cooperate with Emacs.
+
+;;; Code:
+
+(require 'window)
+(require 'buffer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Model all open terminals within a dictionary.
+
+(defconst wpc-terminal/name
+  "wpc/terminal"
+  "The name of my terminal buffers.")
+
+(defun wpc-terminal/find-window ()
+  "Return a reference to an existing terminal window or nil."
+  (->> wpc-terminal/name
+       wpc/add-earmuffs
+       window/find))
+
+(defun wpc-terminal/find-buffer ()
+  "Return a reference to an existing terminal buffer."
+  (->> wpc-terminal/name
+       wpc/add-earmuffs
+       buffer/find))
+
+(defun wpc-terminal/find-or-create ()
+  "Find or create a terminal window."
+  (let ((buffer (wpc-terminal/find-buffer)))
+    (if buffer
+        (buffer/show buffer)
+      (ansi-term "/usr/bin/zsh" wpc-terminal/name))))
+
+;; TODO: Focus terminal after toggling it.
+(defun wpc-terminal/toggle ()
+  "Toggle a custom terminal session in Emacs."
+  (interactive)
+  (let ((window (wpc-terminal/find-window)))
+    (if window
+        (window/delete window)
+      (wpc-terminal/find-or-create))))
+
+(provide 'wpc-terminal)
+;;; wpc-terminal.el ends here
diff --git a/configs/shared/.emacs.d/wpc/packages/wpc-ui.el b/configs/shared/.emacs.d/wpc/packages/wpc-ui.el
new file mode 100644
index 0000000000..4c850e5b4b
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/packages/wpc-ui.el
@@ -0,0 +1,185 @@
+;;; wpc-ui.el --- Any related to the UI/UX goes here -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Hosts font settings, scrolling, color schemes.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'alist)
+(require 'wallpaper)
+(require 'fonts)
+(require 'themes)
+(require 'window-manager)
+(require 'device)
+(require 'laptop-battery)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; increase line height
+(setq-default line-spacing 4)
+
+;; Ensure that buffers update when their contents change on disk.
+(global-auto-revert-mode t)
+
+;; smooth scrolling settings
+(setq scroll-step 1
+      scroll-conservatively 10000)
+
+;; clean up modeline
+(use-package diminish
+  :config
+  (diminish 'emacs-lisp-mode "elisp")
+  (diminish 'evil-commentary-mode)
+  (diminish 'flycheck-mode)
+  (diminish 'auto-revert-mode)
+  (diminish 'which-key-mode)
+  (diminish 'yas-minor-mode)
+  (diminish 'lispyville-mode)
+  (diminish 'undo-tree-mode)
+  (diminish 'company-mode)
+  (diminish 'projectile-mode)
+  (diminish 'eldoc-mode)
+  ;; This is how to diminish `auto-fill-mode'.
+  (diminish 'auto-fill-function)
+  (diminish 'counsel-mode)
+  (diminish 'ivy-mode))
+
+;; disable startup screen
+(setq inhibit-startup-screen t)
+
+;; disable toolbar
+(tool-bar-mode -1)
+
+;; TODO: Re-enable `linum-mode' when I figure out why the theming is so ugly.
+;; enable line numbers
+;; (general-add-hook '(prog-mode-hook
+;;                     text-mode-hook
+;;                     conf-mode-hook)
+;;                   (enable linum-mode))
+
+;; set default buffer for Emacs
+(setq initial-buffer-choice constants/current-project)
+
+;; integration with wpgtk (in vendor directory)
+;; TODO: Re-enable this when base16-wpgtk are looking better.
+;; (require 'wpgtk-theme)
+
+;; base-16 themes to integrate with wpgtk
+;; (use-package base16-theme
+;;   :config
+;;   (require 'wpgtk)
+;;   (colorscheme/set 'base16-wpgtk))
+
+;; premium Emacs themes
+(use-package doom-themes
+  :config
+  (setq doom-themes-enable-bold t
+        doom-themes-enable-italic t)
+  (doom-themes-visual-bell-config)
+  (doom-themes-org-config))
+
+;; file browsing
+(use-package neotree
+  :config
+  (global-set-key [f8] #'neotree-toggle))
+
+;; kbd discovery
+(use-package which-key
+  :config
+  (setq which-key-idle-delay 0.25)
+  (which-key-mode))
+
+;; completion framework
+(use-package ivy
+  ;; TODO: Restore behavior where `counsel' is used everywhere.
+  :config
+  (counsel-mode t)
+  (alist/set! #'counsel-M-x "" ivy-initial-inputs-alist)
+  ;; prefer using `helpful' variants
+  (progn
+    (setq counsel-describe-function-function #'helpful-callable)
+    (setq counsel-describe-variable-function #'helpful-variable))
+  (general-define-key
+   :keymaps 'ivy-minibuffer-map
+   ;; prev
+   "C-k" #'ivy-previous-line
+   "<backtab>" #'ivy-previous-line
+   ;; next
+   "C-j" #'ivy-next-line
+   "<tab>" #'ivy-next-line))
+
+(use-package ivy-prescient
+  :config
+  (ivy-prescient-mode 1)
+  (prescient-persist-mode 1))
+
+;; all-the-icons
+(use-package all-the-icons
+  :config
+  ;; Only run this once after installing.
+  ;; (all-the-icons-install-fonts)
+  )
+
+;; icons for Ivy
+(use-package all-the-icons-ivy
+  :after (ivy all-the-icons)
+  :config
+  (all-the-icons-ivy-setup))
+
+;; disable menubar
+(menu-bar-mode -1)
+(when (string-equal system-type "darwin")
+  (setq ns-auto-hide-menu-bar t))
+
+;; reduce noisiness of auto-revert-mode
+(setq auto-revert-verbose nil)
+
+;; highlight lines that are over 100 characters long
+(use-package whitespace
+  :config
+  (setq whitespace-line-column constants/fill-column)
+  (setq whitespace-style '(face lines-tail))
+  (add-hook 'prog-mode-hook #'whitespace-mode))
+
+;; rebalance emacs windows after splits are created
+;; (defadvice split-window-below (after rebalance-windows activate)
+;;   (balance-windows))
+;; (defadvice split-window-right (after rebalance-windows activate)
+;;   (balance-windows))
+;; (defadvice delete-window (after rebalance-window activate)
+;;   (balance-windows))
+
+;; dirname/filename instead of filename<dirname>
+(setq uniquify-buffer-name-style 'forward)
+
+;; highlight matching parens, brackets, etc
+(show-paren-mode 1)
+
+;; hide the scroll-bars in the GUI
+(scroll-bar-mode -1)
+
+;; GUI alerts in emacs
+(use-package alert
+  :commands (alert)
+  :config
+  (setq alert-default-style 'notifier))
+
+;; TODO: Should `device/work-laptop?' be a function or a constant that gets set
+;; during initialization?
+(when (device/work-laptop?)
+  (laptop-battery/display))
+
+;; Load a theme
+(->> (themes/random)
+     themes/set)
+
+(provide 'wpc-ui)
+;;; wpc-ui.el ends here
diff --git a/configs/shared/.emacs.d/wpc/playback.el b/configs/shared/.emacs.d/wpc/playback.el
new file mode 100644
index 0000000000..9ab1e30ef0
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/playback.el
@@ -0,0 +1,25 @@
+;;; playback.el --- Control playback with Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; As you know, my whole universe is turning Elisp, so this should too!
+
+;;; Code:
+
+(defun playback/prev ()
+  "Move to the previous song."
+  (interactive)
+  (shell-command "playerctl previous"))
+
+(defun playback/next ()
+  "Move to the next song."
+  (interactive)
+  (shell-command "playerctl next"))
+
+(defun playback/play-pause ()
+  "Play or pause the current song."
+  (interactive)
+  (shell-command "playerctl play-pause"))
+
+(provide 'playback)
+;;; playback.el ends here
diff --git a/configs/shared/.emacs.d/wpc/polymorphism.el b/configs/shared/.emacs.d/wpc/polymorphism.el
new file mode 100644
index 0000000000..09045f7fb2
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/polymorphism.el
@@ -0,0 +1,37 @@
+;;; polymorphism.el --- Sketching my ideas for polymorphism in Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Once again: modelled after Elixir.
+
+;;; Code:
+
+;; More sketches of Elisp polymorphism initiative.
+;;
+;; Two macros:
+;; - `defprotocol'
+;; - `definstance'
+;;
+;; Is it just a coincidence that these two macros have the same number of
+;;characters or is that fate?  I say fate.
+;;
+;; (defprotocol monoid
+;;   :functions (empty concat))
+;;
+;; (definstance monoid vector
+;;   :empty
+;;   (lambda () [])
+;;   :concat
+;;   #'vector/concat)
+;;
+;; More sketching...
+;; (defun monoid/empty ()
+;;   "Sketch."
+;;   (funcall #'(,(monoid/classify)/empty)))
+;; (defun monoid/concat (xs)
+;;   "Sketch."
+;;   (apply #'(,(monoid/classify)/concat) args))
+
+
+(provide 'polymorphism)
+;;; polymorphism.el ends here
diff --git a/configs/shared/.emacs.d/wpc/prelude.el b/configs/shared/.emacs.d/wpc/prelude.el
new file mode 100644
index 0000000000..fb45962789
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/prelude.el
@@ -0,0 +1,122 @@
+;;; prelude.el --- My attempt at augmenting Elisp stdlib -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Some of these ideas are scattered across other modules like `fs',
+;; `string-functions', etc.  I'd like to keep everything modular.  I still don't
+;; have an answer for which items belond in `misc'; I don't want that to become
+;; a dumping grounds.  Ideally this file will `require' all other modules and
+;; define just a handful of functions.
+
+;; TODO: Consider removing all dependencies from prelude.el.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Third-party libraries
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 's)
+(require 'dash)
+(require 'f)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Libraries
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Maybe don't globally import everything here.  Disable these and attepmt
+;; to reload Emacs to assess damage.
+(require 'string)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Utilities
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun prelude/to-string (x)
+  "Convert X to a string."
+  (format "%s" x))
+
+(defun prelude/inspect (&rest args)
+  "Message `ARGS' where ARGS are any type."
+  (->> args
+       (list/map #'prelude/to-string)
+       (apply #'string/concat)
+       message))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Assertions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Should I `throw' instead of `error' here?
+(defmacro prelude/assert (x)
+  "Errors unless X is t.
+These are strict assertions and purposely do not rely on truthiness."
+  (let ((as-string (prelude/to-string x)))
+    `(unless (equal t ,x)
+       (error (string/concat "Assertion failed: " ,as-string)))))
+
+(defmacro prelude/refute (x)
+  "Errors unless X is nil."
+  (let ((as-string (prelude/to-string x)))
+    `(unless (equal nil ,x)
+       (error (string/concat "Refutation failed: " ,as-string)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Adapter functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun prelude/identity (x)
+  "Return X unchanged."
+  x)
+
+(defun prelude/const (x)
+  "Return a variadic lambda that will return X."
+  (lambda (&rest _) x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Consider packaging these into a linum-color.el package.
+;; TODO: Generate the color used here from the theme.
+(defvar linum/safe? nil
+  "Flag indicating whether or not it is safe to work with `linum-mode'.")
+
+(defvar linum/mru-color nil
+  "Stores the color most recently attempted to be applied.")
+
+(add-hook 'linum-mode-hook
+          (lambda ()
+            (setq linum/safe? t)
+            (when (maybe/some? linum/mru-color)
+              (set-face-foreground 'linum linum/mru-color))))
+
+(defun prelude/set-line-number-color (color)
+  "Safely set linum color to `COLOR'.
+
+If this is called before Emacs initializes, the color will be stored in
+`linum/mru-color' and applied once initialization completes.
+
+Why is this safe?
+If `(set-face-foreground 'linum)' is called before initialization completes,
+Emacs will silently fail.  Without this function, it is easy to introduce
+difficult to troubleshoot bugs in your init files."
+  (if linum/safe?
+      (set-face-foreground 'linum color)
+    (setq linum/mru-color color)))
+
+(defun prelude/prompt (prompt)
+  "Read input from user with PROMPT."
+  (read-string prompt))
+
+(defun prelude/executable-exists? (name)
+  "Return t if CLI tool NAME exists according to `exec-path'."
+  (let ((file (locate-file name exec-path)))
+    (if (maybe/some? file)
+        (f-exists? file)
+      nil)))
+
+(provide 'prelude)
+;;; prelude.el ends here
diff --git a/configs/shared/.emacs.d/wpc/prelude.nix b/configs/shared/.emacs.d/wpc/prelude.nix
new file mode 100644
index 0000000000..626d4526a2
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/prelude.nix
@@ -0,0 +1,11 @@
+{ pkgs ? import (builtins.fetchTarball
+  "https://github.com/tazjin/depot/archive/master.tar.gz") {} }:
+
+# Ciruclar dependency warning: list.nix depends on prelude.nix, which depends on
+# list.nix.
+pkgs.writeElispBin {
+  name = "prelude";
+  # If this can build with epkgs.ht, remove `(require 'ht)`.
+  deps = epkgs: [ epkgs.s epkgs.dash epkgs.f ./string.nix ./list.nix ];
+  src = ./prelude.el;
+}
diff --git a/configs/shared/.emacs.d/wpc/productivity-timer.el b/configs/shared/.emacs.d/wpc/productivity-timer.el
new file mode 100644
index 0000000000..6d421dd69e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/productivity-timer.el
@@ -0,0 +1,22 @@
+;;; productivity-timer.el --- Commonly used intervals for setting alarms while working -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Select common timer intervals with dmenu and play an alarm sound when
+;; finished.
+;;
+;; This is heavily inspired by iOS's timer feature.
+
+
+;;; Code:
+
+(defconst productivity-timer/intervals
+  '(1 2 3 4 5 10 15 20 30 45 60 120)
+  "Commonly used intervals for timer amounts.")
+
+;; `sleep-for' doesn't seem to work.  Perhaps `sit-for' won't be any better.
+;; How can I use dunst to alert?
+;; `run-at-time' may be the most promising option
+
+(provide 'productivity-timer)
+;;; productivity-timer.el ends here
diff --git a/configs/shared/.emacs.d/wpc/pulse-audio.el b/configs/shared/.emacs.d/wpc/pulse-audio.el
new file mode 100644
index 0000000000..7dff888e98
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/pulse-audio.el
@@ -0,0 +1,48 @@
+;;; pulse-audio.el --- Control audio with Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Because everything in my configuration is turning into Elisp these days.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst pulse-audio/install-kbds? t
+  "When t, install keybindings defined herein.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun pulse-audio/toggle-mute ()
+  "Mute the default sink."
+  (interactive)
+  (shell-command "pactl set-sink-mute @DEFAULT_SINK@ toggle")
+  (message (string/format "[pulse-audio.el] Mute toggled.")))
+
+(defun pulse-audio/lower-volume ()
+  "Low the volume output of the default sink."
+  (interactive)
+  (shell-command "pactl set-sink-volume @DEFAULT_SINK@ -10%")
+  (message (string/format "[pulse-audio.el] Volume lowered.")))
+
+(defun pulse-audio/raise-volume ()
+  "Raise the volume output of the default sink."
+  (interactive)
+  (shell-command "pactl set-sink-volume @DEFAULT_SINK@ +10%")
+  (message (string/format "[pulse-audio.el] Volume raised.")))
+
+
+(when pulse-audio/install-kbds?
+  (exwm-input-set-key
+   (kbd "<XF86AudioMute>") #'pulse-audio/toggle-mute)
+  (exwm-input-set-key
+   (kbd "<XF86AudioLowerVolume>") #'pulse-audio/lower-volume)
+  (exwm-input-set-key
+   (kbd "<XF86AudioRaiseVolume>") #'pulse-audio/raise-volume))
+
+(provide 'pulse-audio)
+;;; pulse-audio.el ends here
diff --git a/configs/shared/.emacs.d/wpc/pushover.el b/configs/shared/.emacs.d/wpc/pushover.el
new file mode 100644
index 0000000000..fb06656cf4
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/pushover.el
@@ -0,0 +1,75 @@
+;;; pushover.el --- Send generic messages to mobile device -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Pushover.net is a mobile app that accepts JSON.  This supports loose
+;; integration between things and mobile devices.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'request)
+(require 'password-store)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst pushover/app-token
+  (password-store-get-field "api-keys/pushover.net" "emacs")
+  "App token for \"emacs\" application.")
+
+(defconst pushover/user-key
+  (password-store-get "api-keys/pushover.net")
+  "Key that identifies me to pushover.")
+
+(defconst pushover/url
+  "https://api.pushover.net/1/messages.json"
+  "URL to POST messages.")
+
+;; TODO: Rename module "pushover".
+
+(defun pushover/notify (message)
+  "Posts MESSAGE to all devices.
+Here are the parameters that Pushover accepts:
+
+Required parameters:
+  - token - your application's API token
+  - user - the user/group key (not e-mail address) of your user (or you),
+    viewable when logged into our dashboard (often referred to as USER_KEY in
+    our documentation and code examples)
+  - message - your message
+
+Additional parameters (optional):
+  - attachment - an image attachment to send with the message; see attachments
+    for more information on how to upload files
+    device - your user's device name to send the message directly to that
+    device, rather than all of the user's devices (multiple devices may be
+    separated by a comma)
+  - title - your message's title, otherwise your app's name is used
+  - url - a supplementary URL to show with your message
+  - url_title - a title for your supplementary URL, otherwise just the URL is
+    shown
+  - priority - send as -2 to generate no notification/alert, -1 to always send
+    as a quiet notification, 1 to display as high-priority and bypass the user's
+    quiet hours, or 2 to also require confirmation from the user
+  - sound - the name of one of the sounds supported by device clients to
+    override the user's default sound choice
+  - timestamp - a Unix timestamp"
+  (request
+   pushover/url
+   :type "POST"
+   :params `(("token"   . ,pushover/app-token)
+             ("user"    . ,pushover/user-key)
+             ("message" . ,message))
+   :data nil
+   :parser 'json-read
+   :success (cl-function
+             (lambda (&key data &allow-other-keys)
+               (message "Pushover.net notification sent!")))))
+
+(provide 'pushover)
+;;; pushover.el ends here
diff --git a/configs/shared/.emacs.d/wpc/random.el b/configs/shared/.emacs.d/wpc/random.el
new file mode 100644
index 0000000000..148506c04d
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/random.el
@@ -0,0 +1,73 @@
+;;; random.el --- Functions for working with randomness -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Functions for working with randomness.  Some of this code is not as
+;; functional as I'd like from.
+
+;;; Code:
+
+(require 'prelude)
+(require 'number)
+(require 'math)
+(require 'series)
+(require 'list)
+(require 'set)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun random/int (x)
+  "Return a random integer from 0 to `X'."
+  (random x))
+
+;; TODO: Make this work with sequences instead of lists.
+(defun random/choice (xs)
+  "Return a random element of `XS'."
+  (let ((ct (list/length xs)))
+    (list/get
+     (random/int ct)
+     xs)))
+
+(defun random/boolean? ()
+  "Randonly return t or nil."
+  (random/choice (list t nil)))
+
+;; TODO: This may not work if any of these generate numbers like 0, 1, etc.
+(defun random/uuid ()
+  "Return a generated UUID string."
+  (let ((eight  (number/dec (math/triangle-of-power :base 16 :power 8)))
+        (four   (number/dec (math/triangle-of-power :base 16 :power 4)))
+        (twelve (number/dec (math/triangle-of-power :base 16 :power 12))))
+    (format "%x-%x-%x-%x-%x"
+            (random/int eight)
+            (random/int four)
+            (random/int four)
+            (random/int four)
+            (random/int twelve))))
+
+(defun random/token (length)
+  "Return a randomly generated hexadecimal string of LENGTH."
+  (->> (series/range 0 (number/dec length))
+       (list/map (lambda (_) (format "%x" (random/int 15))))
+       (list/join "")))
+
+;; TODO: Support random/sample
+(defun random/sample (n xs)
+  "Return a randomly sample of list XS of size N."
+  (prelude/assert (and (>= n 0) (< n (list/length xs))))
+  (cl-labels ((do-sample
+               (n xs y ys)
+               (if (= n (set/count ys))
+                   (->> ys
+                        set/to-list
+                        (list/map (lambda (i)
+                                    (list/get i xs))))
+                 (if (set/contains? y ys)
+                     (do-sample n xs (random/int (list/length xs)) ys)
+                   (do-sample n xs y (set/add y ys))))))
+    (do-sample n xs (random/int (list/length xs)) (set/new))))
+
+(provide 'random)
+;;; random.el ends here
diff --git a/configs/shared/.emacs.d/wpc/scheduler.el b/configs/shared/.emacs.d/wpc/scheduler.el
new file mode 100644
index 0000000000..bae9532289
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/scheduler.el
@@ -0,0 +1,22 @@
+;;; scheduler.el --- Sketches of scheduling -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Attempting to create a FSM for scheduling things in various ways:
+;;
+;; Scheduling policies:
+;; - earliest due date: minimizes total lateness of all tasks in a pool.  Put
+;;   the task with the latest due date last in the list and work backwards to
+;;   solve the precedence constraint (i.e. dependency issue).
+;; - shortest processing time: maximizes number of tasks completed.  Prioritize
+;;   tasks in the order of how long they will take to complete from shortest to
+;;   longest.  This breaks down when precedence constraints are introduced.
+;;
+;; Tasks should inherit prioritization.
+
+
+
+;;; Code:
+
+(provide 'scheduler)
+;;; scheduler.el ends here
diff --git a/configs/shared/.emacs.d/wpc/scope.el b/configs/shared/.emacs.d/wpc/scope.el
new file mode 100644
index 0000000000..48aa85ad0e
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/scope.el
@@ -0,0 +1,99 @@
+;;; scope.el --- Work with a scope data structure -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Exposing an API for working with a scope data structure in a non-mutative
+;; way.
+;;
+;; What's a scope?  Think of a scope as a stack of key-value bindings.
+
+;;; Code:
+
+(require 'alist)
+(require 'stack)
+(require 'struct)
+(require 'macros)
+
+(cl-defstruct scope scopes)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun scope/new ()
+  "Return an empty scope."
+  (make-scope :scopes (->> (stack/new)
+                           (stack/push (alist/new)))))
+
+(defun scope/flatten (xs)
+  "Return a flattened representation of the scope, XS.
+The newest bindings eclipse the oldest."
+  (->> xs
+       scope-scopes
+       stack/to-list
+       (list/reduce (alist/new)
+                    (lambda (scope acc)
+                      (alist/merge acc scope)))))
+
+(defun scope/push-new (xs)
+  "Push a new, empty scope onto XS."
+  (struct/update scope
+                 scopes
+                 (>> (stack/push (alist/new)))
+                 xs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Read
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun scope/get (k xs)
+  "Return K from XS if it's in scope."
+  (->> xs
+       scope/flatten
+       (alist/get k)))
+
+(defun scope/current (xs)
+  "Return the newest scope from XS."
+  (let ((xs-copy (copy-scope xs)))
+    (->> xs-copy
+         scope-scopes
+         stack/peek)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Update
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun scope/set (k v xs)
+  "Set value, V, at key, K, in XS for the current scope."
+  (struct/update scope
+                 scopes
+                 (>> (stack/map-top (>> (alist/set k v))))
+                 xs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Delete
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun scope/pop (xs)
+  "Return a new scope without the top element from XS."
+  (->> xs
+       scope-scopes
+       stack/pop))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun scope/defined? (k xs)
+  "Return t if K is in scope of XS."
+  (->> xs
+       scope/flatten
+       (alist/has-key? k)))
+
+;; TODO: Find a faster way to write aliases like this.
+(defun scope/instance? (xs)
+  "Return t if XS is a scope struct."
+  (scope-p xs))
+
+(provide 'scope)
+;;; scope.el ends here
diff --git a/configs/shared/.emacs.d/wpc/screen-brightness.el b/configs/shared/.emacs.d/wpc/screen-brightness.el
new file mode 100644
index 0000000000..1b74ea34a9
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/screen-brightness.el
@@ -0,0 +1,57 @@
+;;; screen-brightness.el --- Control laptop screen brightness -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Mainly just Elisp wrappers around `xbacklight`.
+
+;;; Code:
+
+;; TODO: Define some isomorphisms. E.g. int->string, string->int.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'string)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst screen-brightness/step-size 15
+  "The size of the increment or decrement step for the screen's brightness.")
+
+(defcustom screen-brightness/install-kbds? t
+  "If t, install the keybindings to control screen brightness.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun screen-brightness/increase ()
+  "Increase the screen brightness."
+  (interactive)
+  (->> screen-brightness/step-size
+       int-to-string
+       (string/concat "xbacklight -inc ")
+       shell-command)
+  (message "[screen-brightness.el] Increased screen brightness."))
+
+(defun screen-brightness/decrease ()
+  "Decrease the screen brightness."
+  (interactive)
+  (->> screen-brightness/step-size
+       int-to-string
+       (string/concat "xbacklight -dec ")
+       shell-command)
+  (message "[screen-brightness.el] Decreased screen brightness."))
+
+(when screen-brightness/install-kbds?
+  (exwm-input-set-key
+   (kbd "<XF86MonBrightnessUp>") #'screen-brightness/increase)
+  (exwm-input-set-key
+   (kbd "<XF86MonBrightnessDown>") #'screen-brightness/decrease))
+
+(provide 'screen-brightness)
+;;; screen-brightness.el ends here
diff --git a/configs/shared/.emacs.d/wpc/sequence.el b/configs/shared/.emacs.d/wpc/sequence.el
new file mode 100644
index 0000000000..a5428ef044
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/sequence.el
@@ -0,0 +1,105 @@
+;;; sequence.el --- Working with the "sequence" types -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Elisp supports a typeclass none as "sequence" which covers the following
+;; types:
+;; - list: '(1 2 3 4 5)
+;; - vector: ["John" 27 :blue]
+;; - string: "To be or not to be..."
+
+;; TODO: Document the difference between a "reduce" and a "fold".  I.e. - reduce
+;; has an initial value whereas fold uses the first element in the sequence as
+;; the initial value.
+;;
+;; Note: This should be an approximation of Elixir's Enum protocol albeit
+;; without streams.
+;;
+;; Elisp has done a lot of this work already and these are mostly wrapper
+;; functions.
+;; See the following list for reference:
+;; - sequencep
+;; - elt
+;; - copy-sequence
+;; - reverse
+;; - nreverse
+;; - sort
+;; - seq-elt
+;; - seq-length
+;; - seqp
+;; - seq-drop
+;; - seq-take
+;; - seq-take-while
+;; - seq-drop-while
+;; - seq-do
+;; - seq-map
+;; - seq-mapn
+;; - seq-filter
+;; - seq-remove
+;; - seq-reduce
+;; - seq-some
+;; - seq-find
+;; - seq-every-p
+;; - seq-empty-p
+;; - seq-count
+;; - seq-sort
+;; - seq-contains
+;; - seq-position
+;; - seq-uniq
+;; - seq-subseq
+;; - seq-concatenate
+;; - seq-mapcat
+;; - seq-partition
+;; - seq-intersection
+;; - seq-difference
+;; - seq-group-by
+;; - seq-into
+;; - seq-min
+;; - seq-max
+;; - seq-doseq
+;; - seq-let
+
+;;; Code:
+
+;; Perhaps we can provide default implementations for `filter' and `map' derived
+;; from the `reduce' implementation.
+;; (defprotocol sequence
+;;   :functions (reduce))
+;; (definstance sequence list
+;;   :reduce #'list/reduce
+;;   :filter #'list/filter
+;;   :map    #'list/map)
+;; (definstance sequence vector
+;;   :reduce #'vector/reduce)
+;; (definstance sequence string
+;;   :reduce #'string)
+
+(defun sequence/classify (xs)
+  "Return the type of `XS'."
+  (cond
+   ((listp xs) 'list)
+   ((vectorp xs) 'vector)
+   ((stringp xs) 'string)))
+
+(defun sequence/reduce (acc f xs)
+  "Reduce of `XS' calling `F' on x and `ACC'."
+  (seq-reduce
+   (lambda (acc x)
+     (funcall f x acc))
+   xs
+   acc))
+
+;; Elixir also turned everything into a list for efficiecy reasons.
+
+(defun sequence/filter (p xs)
+  "Filter `XS' with predicate, `P'.
+Returns a list regardless of the type of `XS'."
+  (seq-filter p xs))
+
+(defun sequence/map (f xs)
+  "Maps `XS' calling `F' on each element.
+Returns a list regardless of the type of `XS'."
+  (seq-map f xs))
+
+(provide 'sequence)
+;;; sequence.el ends here
diff --git a/configs/shared/.emacs.d/wpc/series.el b/configs/shared/.emacs.d/wpc/series.el
new file mode 100644
index 0000000000..977ffc5026
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/series.el
@@ -0,0 +1,81 @@
+;;; series.el --- Hosting common series of numbers -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Encoding number series as I learn about them.
+;;
+;; These are the following series I'm interested in supporting:
+;; - Fibonacci
+;; - Catalan numbers
+;; - Figurate number series
+;;   - Triangular
+;;   - Square
+;;   - Pentagonal
+;;   - Hexagonal
+;;   - Lazy-caterer
+;; - Magic square
+;; - Look-and-say
+
+;;; Code:
+
+(require 'number)
+
+(defun series/range (beg end)
+  "Create a list of numbers from `BEG' to `END'.
+This is an inclusive number range."
+  (if (< end beg)
+      (list/reverse
+       (number-sequence end beg))
+    (number-sequence beg end)))
+
+(defun series/fibonacci-number (i)
+  "Return the number in the fibonacci series at `I'."
+  (cond
+   ((= 0 i) 0)
+   ((= 1 i) 1)
+   (t (+ (series/fibonacci-number (- i 1))
+         (series/fibonacci-number (- i 2))))))
+
+(defun series/fibonacci (n)
+  "Return the first `N' numbers of the fibonaccci series starting at zero."
+  (if (= 0 n)
+      '()
+    (list/reverse
+     (list/cons (series/fibonacci-number (number/dec n))
+                (list/reverse
+                 (series/fibonacci (number/dec n)))))))
+
+;; TODO: Consider memoization.
+(defun series/triangular-number (i)
+  "Return the number in the triangular series at `I'."
+  (if (= 0 i)
+      0
+    (+ i (series/triangular-number (number/dec i)))))
+
+;; TODO: Improve performance.
+;; TODO: Consider creating a stream protocol with `stream/next' and implement
+;; this using that.
+(defun series/triangular (n)
+  "Return the first `N' numbers of a triangular series starting at 0."
+  (if (= 0 n)
+      '()
+    (list/reverse
+     (list/cons (series/triangular-number (number/dec n))
+                (list/reverse
+                 (series/triangular (number/dec n)))))))
+
+(defun series/catalan-number (i)
+  "Return the catalan number in the series at `I'."
+  (if (= 0 i)
+      1
+    (/ (number/factorial (* 2 i))
+       (* (number/factorial (number/inc i))
+          (number/factorial i)))))
+
+(defun series/catalan (n)
+  "Return the first `N' numbers in a catalan series."
+  (->> (series/range 0 (number/dec n))
+       (list/map #'series/catalan-number)))
+
+(provide 'series)
+;;; series.el ends here
diff --git a/configs/shared/.emacs.d/wpc/set.el b/configs/shared/.emacs.d/wpc/set.el
new file mode 100644
index 0000000000..fd86f9033c
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/set.el
@@ -0,0 +1,92 @@
+;;; set.el --- Working with mathematical sets -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; The set data structure is a collection that deduplicates its elements.
+
+;;; Code:
+
+(require 'ht) ;; friendlier API for hash-tables
+(require 'dotted)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Wish List
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; - TODO: Support enum protocol for set.
+;; - TODO: Prefer a different hash-table library that doesn't rely on mutative
+;;   code.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct set xs)
+
+(defun set/from-list (xs)
+  "Create a new set from the list XS."
+  (make-set :xs (->> xs
+                     (list/map #'dotted/new)
+                     ht-from-alist)))
+
+(defun set/new (&rest args)
+  "Create a new set from ARGS."
+  (set/from-list args))
+
+(defun set/to-list (xs)
+  "Map set XS into a list."
+  (->> xs
+       set-xs
+       ht-keys))
+
+(defun set/add (x xs)
+  "Add X to set XS."
+  (struct/update set
+                 xs
+                 (lambda (table)
+                   (let ((table-copy (ht-copy table)))
+                     (ht-set table-copy x 10)
+                     table-copy))
+                 xs))
+
+(defun set/count (xs)
+  "Return the number of elements in XS."
+  (->> xs
+       set-xs
+       ht-size))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun set/empty? (xs)
+  "Return t if XS has no elements in it."
+  (= 0 (set/count xs)))
+
+(defun set/contains? (x xs)
+  "Return t if set XS has X."
+  (ht-contains? (set-xs xs) x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst set/enable-testing? t
+  "Run tests when t.")
+
+(when set/enable-testing?
+  (progn
+    ;; {from,to}-list
+    (prelude/assert (equal '(1 2 3)
+                           (->> '(1 1 2 2 3 3)
+                                set/from-list
+                                set/to-list)))
+    ;; empty?
+    (prelude/assert (set/empty? (set/new)))
+    (prelude/refute (set/empty? (set/new 1 2 3)))
+    ;; count
+    (prelude/assert (= 0 (set/count (set/new))))
+    (prelude/assert (= 2 (set/count (set/new 1 1 2 2))))))
+
+(provide 'set)
+;;; set.el ends here
diff --git a/configs/shared/.emacs.d/wpc/sre.el b/configs/shared/.emacs.d/wpc/sre.el
new file mode 100644
index 0000000000..1c8f6ddd9a
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/sre.el
@@ -0,0 +1,26 @@
+;;; sre.el --- Site Reliability Engineering stuffs -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Storing some data that might be helpful in my ladder switch attempt.
+
+;;; Code:
+
+(defvar sre/introduction-email
+  "Hello!
+
+My name is William Carroll. I'm currently attempting a ladder switch. I have my
+manager's approval to look for a new role because we believe I have been hired
+for the wrong position.
+
+I'm eager to move ahead if there are any SRE openings in LON that fit my
+profile. I'm happy to share more information with you about my background and
+what I'm looking for. I've been attending the SRE Ops Review meetings in 6PS
+weekly for awhile now, so we should be in the same office every Tuesday if
+meeting in person is easier for you.
+
+Let me know!"
+  "Boilerplate email for reaching out to SRE hiring managers.")
+
+(provide 'sre)
+;;; sre.el ends here
diff --git a/configs/shared/.emacs.d/wpc/ssh.el b/configs/shared/.emacs.d/wpc/ssh.el
new file mode 100644
index 0000000000..d703937573
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/ssh.el
@@ -0,0 +1,31 @@
+;;; ssh.el --- When working remotely -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Configuration to make remote work easier.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tramp)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Is "ssh" preferable to "scp"?
+(setq tramp-default-method "ssh")
+
+;; Taken from: https://superuser.com/questions/179313/tramp-waiting-for-prompts-from-remote-shell
+(setq tramp-shell-prompt-pattern "^[^$>\n]*[#$%>] *\\(\[[0-9;]*[a-zA-Z] *\\)*")
+
+;; TODO: Re-enable this in case "dumb" isn't the default.
+;; (setq tramp-terminal-type "dumb")
+
+(setq tramp-verbose 10)
+
+(provide 'ssh)
+;;; ssh.el ends here
diff --git a/configs/shared/.emacs.d/wpc/stack.el b/configs/shared/.emacs.d/wpc/stack.el
new file mode 100644
index 0000000000..052ed881d2
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/stack.el
@@ -0,0 +1,93 @@
+;;; stack.el --- Working with stacks in Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; A stack is a LIFO queue.
+;; The design goal here is to expose an intuitive API for working with stacks in
+;; non-mutative way.
+;;
+;; TODO: Consider naming a Functor instance "Mappable."
+;; TODO: Consider naming a Foldable instance "Reduceable."
+;;
+;; TODO: Consider implementing an instance for Mappable.
+;; TODO: Consider implementing an instance for Reduceable.
+
+;;; Code:
+
+(require 'list)
+
+(cl-defstruct stack xs)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun stack/new ()
+  "Create an empty stack."
+  (make-stack :xs '()))
+
+(defun stack/from-list (xs)
+  "Create a new stack from the list, `XS'."
+  (list/reduce (stack/new) #'stack/push xs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Read
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun stack/peek (xs)
+  "Look at the top element of `XS' without popping it off."
+  (->> xs
+       stack-xs
+       list/head))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Update
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun stack/push (x xs)
+  "Push `X' on `XS'."
+  (struct/update stack
+                 xs
+                 (>> (list/cons x))
+                 xs))
+
+;; TODO: How to return something like {(list/head xs), (list/tail xs)} in Elixir
+;; TODO: How to handle popping from empty stacks?
+(defun stack/pop (xs)
+  "Return the stack, `XS', without the top element.
+Since I cannot figure out a nice way of return tuples in Elisp, if you want to
+look at the first element, use `stack/peek' before running `stack/pop'."
+  (struct/update stack
+                 xs
+                 (>> list/tail)
+                 xs))
+
+(defun stack/map-top (f xs)
+  "Apply F to the top element of XS."
+  (->> xs
+       stack/pop
+       (stack/push (funcall f (stack/peek xs)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun stack/to-list (xs)
+  "Return XS as a list.
+The round-trip property of `stack/from-list' and `stack/to-list' should hold."
+  (->> xs
+       stack-xs
+       list/reverse))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Create a macro that wraps `cl-defstruct' that automatically creates
+;; things like `new', `instance?'.
+(defun stack/instance? (xs)
+  "Return t if XS is a stack."
+  (stack-p xs))
+
+(provide 'stack)
+;;; stack.el ends here
diff --git a/configs/shared/.emacs.d/wpc/string.el b/configs/shared/.emacs.d/wpc/string.el
new file mode 100644
index 0000000000..56c9279dd5
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/string.el
@@ -0,0 +1,126 @@
+;; string.el --- Library for working with strings -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Library for working with string.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 's)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Strings
+(defun string/hookify (x)
+  "Append \"-hook\" to X."
+  (s-append "-hook" x))
+
+(defun string/ensure-hookified (x)
+  "Ensure that X has \"-hook\" appended to it."
+  (if (s-ends-with? "-hook" x)
+      x
+    (string/hookify x)))
+
+(defun string/format (x &rest args)
+  "Format template string X with ARGS."
+  (apply #'format (cons x args)))
+
+(defun string/concat (&rest strings)
+  "Joins `STRINGS' into onto string."
+  (apply #'s-concat strings))
+
+(defun string/->symbol (string)
+  "Maps `STRING' to a symbol."
+  (intern string))
+
+(defun string/<-symbol (symbol)
+  "Maps `SYMBOL' into a string."
+  (symbol-name symbol))
+
+(defun string/prepend (prefix x)
+  "Prepend `PREFIX' onto `X'."
+  (s-concat prefix x))
+
+(defun string/append (postfix x)
+  "Appen `POSTFIX' onto `X'."
+  (s-concat x postfix))
+
+(defun string/surround (s x)
+  "Surrounds `X' one each side with `S'."
+  (->> x
+       (string/prepend s)
+       (string/append s)))
+
+;; TODO: Define a macro for defining a function and a test.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Casing
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun string/caps->kebab (x)
+  "Change the casing of `X' from CAP_CASE to kebab-case."
+  (->> x
+       s-downcase
+       (s-replace "_" "-")))
+
+(defun string/kebab->caps (x)
+  "Change the casing of X from CAP_CASE to kebab-case."
+  (->> x
+       s-upcase
+       (s-replace "-" "_")))
+
+(defun string/lower->caps (x)
+  "Change the casing of X from lowercase to CAPS_CASE."
+  (->> x
+       s-upcase
+       (s-replace " " "_")))
+
+(defun string/lower->kebab (x)
+  "Change the casing of `X' from lowercase to kebab-case."
+  (s-replace " " "-" x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun string/instance? (x)
+  "Return t if X is a string."
+  (stringp x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support these.
+;; (require 'macros)
+;; (macros/test
+;;  :function string/surround
+;;  :test "works as expected"
+;;  :args '("-*-" "surround")
+;;  :expect "-*-surround-*-"
+;;  :equality string=)
+;
+;; (macros/test
+;;  :function string/caps->kebab
+;;  :test "works as expected"
+;;  :args '("CAPS_CASE_STRING")
+;;  :expect "caps-case-string"
+;;  :equality string=)
+;
+;; ;; TODO: Generate :test from docs of defun.
+;; (macros/test
+;;  :function string/kebab->caps
+;;  :test "works as expected"
+;;  :args '("kebab-case-string")
+;;  :expect "KEBAB_CASE_STRING"
+;;  :equality =)
+
+(provide 'string)
+;;; string.el ends here
diff --git a/configs/shared/.emacs.d/wpc/string.nix b/configs/shared/.emacs.d/wpc/string.nix
new file mode 100644
index 0000000000..1f815b26bb
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/string.nix
@@ -0,0 +1,8 @@
+{ pkgs ? import (builtins.fetchTarball
+  "https://github.com/tazjin/depot/archive/master.tar.gz") {} }:
+
+pkgs.writeElispBin {
+  name = "string";
+  deps = epkgs: [ epkgs.dash epkgs.s ./prelude.nix ];
+  src = ./string.el;
+}
diff --git a/configs/shared/.emacs.d/wpc/struct.el b/configs/shared/.emacs.d/wpc/struct.el
new file mode 100644
index 0000000000..248d08e97c
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/struct.el
@@ -0,0 +1,71 @@
+;;; struct.el --- Helpers for working with structs -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Provides new macros for working with structs.  Also provides adapter
+;; interfaces to existing struct macros, that should have more intuitive
+;; interfaces.
+;;
+;; Sometimes `setf' just isn't enough.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Wish list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; - TODO: Replace `symbol-name' and `intern' calls with isomorphism.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'string)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro struct/update (type field f xs)
+  "Apply F to FIELD in XS, which is a struct of TYPE.
+This is immutable."
+  (let ((copier (->> type
+                     symbol-name
+                     (string/prepend "copy-")
+                     intern))
+        (accessor (->> field
+                       symbol-name
+                       (string/prepend (string/concat (symbol-name type) "-"))
+                       intern)))
+    `(let ((copy (,copier ,xs)))
+       (setf (,accessor copy) (funcall ,f (,accessor copy)))
+       copy)))
+
+(defmacro struct/set (type field x xs)
+  "Immutably set FIELD in XS (struct TYPE) to X."
+  (let ((copier (->> type
+                     symbol-name
+                     (string/prepend "copy-")
+                     intern))
+        (accessor (->> field
+                       symbol-name
+                       (string/prepend (string/concat (symbol-name type) "-"))
+                       intern)))
+    `(let ((copy (,copier ,xs)))
+       (setf (,accessor copy) ,x)
+       copy)))
+
+(defmacro struct/set! (type field x xs)
+  "Set FIELD in XS (struct TYPE) to X mutably.
+This is an adapter interface to `setf'."
+  (let ((accessor (->> field
+                       symbol-name
+                       (string/prepend (string/concat (symbol-name type) "-"))
+                       intern)))
+    `(progn
+       (setf (,accessor xs) ,x)
+       xs)))
+
+(provide 'struct)
+;;; struct.el ends here
diff --git a/configs/shared/.emacs.d/wpc/symbol.el b/configs/shared/.emacs.d/wpc/symbol.el
new file mode 100644
index 0000000000..9119b29470
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/symbol.el
@@ -0,0 +1,43 @@
+;; symbol.el --- Library for working with symbols. -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Library for working with symbols.
+
+;;; Code:
+
+;; TODO: Why is ivy mode map everywhere?
+
+(require 'string)
+
+;; Symbols
+(defun symbol/as-string (callback x)
+  "Treat the symbol, X, as a string while applying CALLBACK to it.
+Coerce back to a symbol on the way out."
+  (->> x
+       #'symbol-name
+       callback
+       #'intern))
+
+(defun symbol/to-string (x)
+  "Map `X' into a string."
+  (string/<-symbol x))
+
+(defun symbol/hookify (x)
+  "Append \"-hook\" to X when X is a symbol."
+  (symbol/as-string #'string/hookify x))
+
+(defun symbol/ensure-hookified (x)
+  "Ensure that X has \"-hook\" appended to it when X is a symbol."
+  (symbol/as-string #'string/ensure-hookified x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun symbol/instance? (x)
+  "Return t if X is a symbol."
+  (symbolp x))
+
+(provide 'symbol)
+;;; symbol.el ends here
diff --git a/configs/shared/.emacs.d/wpc/themes.el b/configs/shared/.emacs.d/wpc/themes.el
new file mode 100644
index 0000000000..e7109deaa9
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/themes.el
@@ -0,0 +1,170 @@
+;;; themes.el --- Functions for working with my themes. -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+
+
+;; Because I couldn't get cycle-themes to work, I'm writing my own version.
+;;
+;; Terminology:
+;; - colorscheme: determines the colors used by syntax highlighting and other
+;;   Emacs UI elements.
+;; - theme: Structural representation of a "theme" that includes colorscheme
+;;   (see above), font, wallpaper.  "theme" is a superset of "colorscheme".
+;;
+;; Wishlist:
+;; - TODO: Find a way to update the terminal (e.g. terminator) theme.
+;; - TODO: Ensure terminal font is updated when Emacs font changes.
+;; - TODO: Support a light theme.
+;; - TODO: Support Rick & Morty theme.
+;; - TODO: Support retro/arcade/80s theme.
+
+;;; Code:
+
+;; Dependencies
+(require 'prelude)
+(require 'alist)
+(require 'symbol)
+(require 'f)
+(require 'wallpaper)
+(require 'fonts)
+(require 'cycle)
+(require 'symbol)
+(require 'random)
+(require 'colorscheme)
+(require 'dotted)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; The theme struct couples a font, a wallpaper, and a colorschemes.
+(cl-defstruct theme
+  font
+  wallpaper
+  colorscheme)
+
+(defvar themes/current nil
+  "Store the name of the currently enabled theme.")
+
+(defconst themes/themes
+  (list (dotted/new
+         "Forest"
+         (make-theme
+          :font "Operator Mono Light"
+          :wallpaper "forest_8k.jpg"
+          :colorscheme 'doom-peacock))
+        (dotted/new
+         "Geometry"
+         (make-theme
+          :font "Input Mono Medium"
+          :wallpaper "geometric_4k.jpg"
+          :colorscheme 'doom-molokai))
+        (dotted/new
+         "Ice"
+         (make-theme
+          :font "Go Mono"
+          :wallpaper "construction_paper_iceberg_4k.jpg"
+          :colorscheme 'doom-dracula))
+        (dotted/new
+         "Lego Manhattan"
+         (make-theme
+          :font "Input Mono Medium"
+          :wallpaper "lego_manhattan.jpg"
+          :colorscheme 'base16-atelier-sulphurpool))
+        (dotted/new
+         "Edison Lightbulb"
+         (make-theme
+          :font "Operator Mono Light"
+          :wallpaper "lightbulb_4k.jpg"
+          :colorscheme 'base16-atelier-cave))
+        (dotted/new
+         "Galaxy"
+         (make-theme
+          :font "Source Code Pro"
+          :wallpaper "galaxy_4k.jpg"
+          :colorscheme 'doom-moonlight))
+        (dotted/new
+         "Underwater"
+         (make-theme
+          :font "Go Mono"
+          ;; TODO: Change this wallpaper to an oceanic scene.
+          :wallpaper "galaxy_4k.jpg"
+          :colorscheme 'doom-solarized-dark))
+        (dotted/new
+         "Fantasy Tree"
+         (make-theme
+          :font "Go Mono"
+          :wallpaper "fantasy_tree_4k.jpg"
+          :colorscheme 'doom-outrun-electric)))
+  "Predefined themes to suit my whims.")
+
+;; TODO: Choose between plural and singular names for Elisp modules.  For
+;; example, why have themes.el and colorscheme.el.  I think singular is
+;; preferable.
+;; TODO: Decide between "message", "show", "print", "inspect" for naming
+;; commands that output human-readable information to the "*Messages*" buffer.
+;; TODO: Is there a idiomatic CL/Elisp way to print struct information?
+(defun themes/print (name)
+  "Print a human-readable description of theme named NAME."
+  (let* ((theme (alist/get name themes/themes))
+         (f (theme-font theme))
+         (w (theme-wallpaper theme))
+         (c (theme-colorscheme theme)))
+    (message (string/format
+              "[themes] Name: %s. Font: %s. Wallpaper: %s. Colorscheme: %s"
+              name f w c))))
+
+;; TODO: Make this into a proper test.
+(defun themes/debug ()
+  "Print a human-readable description of theme named NAME."
+  (interactive)
+  (let ((theme (alist/get themes/current themes/themes)))
+    (prelude/assert (equal (theme-font theme)
+                           (fonts/current)))
+    (prelude/assert (equal (theme-wallpaper theme)
+                           (f-filename (wallpaper/current))))
+    (prelude/assert (equal (theme-colorscheme theme)
+                           (colorscheme/current)))
+    (message "[themes] Debug couldn't find any inconsistencies. All good!")))
+
+;; TODO: Assert that all of the dependencies exist before attempting to load
+;; theme.
+;; TODO: Provide a friendlier way to define themes.
+(defun themes/ivy-select ()
+  "Use ivy to interactively load a theme."
+  (interactive)
+  (let* ((name (ivy-read "Theme: " (alist/keys themes/themes))))
+    (message (string/format "name: %s" name))
+    (themes/set name)))
+
+(defun themes/load (theme)
+  "Load the struct, THEME."
+  (colorscheme/disable-all)
+  (let* ((font (theme-font theme))
+         (wallpaper (theme-wallpaper theme))
+         (colorscheme (theme-colorscheme theme)))
+    (fonts/whitelist-set font)
+    (wallpaper/whitelist-set (f-join wallpaper/path-to-dir wallpaper))
+    (colorscheme/whitelist-set colorscheme)))
+
+(defun themes/set (name)
+  "Set the currently enabled theme to the theme named NAME.
+NAME needs to a key defined in `themes/themes'."
+  (prelude/assert (alist/has-key? name themes/themes))
+  (themes/load (alist/get name themes/themes))
+  (setq themes/current name))
+
+(defun themes/print-current ()
+  "Print the currently enabled theme."
+  (interactive)
+  (themes/print themes/current))
+
+(defun themes/random ()
+  "Return the name of a randomly selected theme in `themes/themes'."
+  (->> themes/themes
+       alist/keys
+       random/choice))
+
+(provide 'themes)
+;;; themes.el ends here
diff --git a/configs/shared/.emacs.d/wpc/todo.el b/configs/shared/.emacs.d/wpc/todo.el
new file mode 100644
index 0000000000..7369f01d82
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/todo.el
@@ -0,0 +1,297 @@
+;;; todo.el --- Bespoke task management system -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Marriage of my personal task-management system, which I've been using for 18
+;; months and is a mixture of handwritten notes, iOS notes, and org-mode files,
+;; with Emacs's famous `org-mode'.
+;;
+;; For me, I'd like a live, reactive state management system.  I'd like
+;; `org-mode' to be a nice way of rendering my TODOs, but I think the
+;; relationship with `org-mode' ends there.
+;;
+;; Intended to supplement my org-mode workflow.
+;;
+;; Wish-list:
+;; - Daily emails for standups
+;; - Templates for commonly occurring tasks
+
+;; Dependencies
+(require 'dash)
+(require 'f)
+(require 'macros)
+
+;;; Code:
+
+;; TODO: Classify habits as 'daily, 'weekly, 'monthly, 'yearly, 'event-driven
+
+;; TODO: Consider serving these values up to a React webpage in Chrome.
+
+;; TODO: Classify meetings as either 'recurrent or 'ad-hoc.
+
+;; TODO: Support sorting by `type'.
+
+;; TODO: Support work-queue idea for "Tomorrow's todos."
+
+;; TODO: Support macro to generate all possible predicates for todo types.
+
+;; TODO: Support export to org-mode file
+
+;; TODO: Support generic way to quickly render a list
+
+(defcustom todo/install-kbds? t
+  "When t, install the keybindings.")
+
+;; TODO: Add documentation.
+(cl-defstruct todo type label)
+
+;; TODO: Consider keeping this in Dropbox.
+;; TODO: Support whether or not the todo is done.
+(defconst todo/org-file-path "~/Dropbox/org/today.org")
+
+;; TODO: Support remaining function for each type.
+;; TODO: Support completed function for each type.
+
+(defun todo/completed? (x)
+  "Return t is `X' is marked complete."
+  (todo-complete x))
+
+;; TODO: Prefer `new-{task,habit,meeting}'.
+
+(defun todo/completed (xs)
+  "Return the todo items in `XS' that are marked complete."
+  (->> xs
+       (-filter #'todo/completed?)))
+
+(defun todo/remaining (xs)
+  "Return the todo items in `XS' that are not marked complete."
+  (->> xs
+       (-reject #'todo/completed?)))
+
+(defun todo/task (label)
+  "Convenience function for creating a task todo with `LABEL'."
+  (make-todo
+   :type 'task
+   :label label))
+
+(defun todo/meeting (label)
+  "Convenience function for creating a meeting todo with `LABEL'."
+  (make-todo
+   :type 'meeting
+   :label label))
+
+(defun todo/habit (label)
+  "Convenience function for creating a habit todo with `LABEL'."
+  (make-todo
+   :type 'habit
+   :label label))
+
+(defun todo/task? (x)
+  "Return t if `X' is a task."
+  (equal 'task (todo-type x)))
+
+(defun todo/habit? (x)
+  "Return t if `X' is a habit."
+  (equal 'habit (todo-type x)))
+
+(defun todo/meeting? (x)
+  "Return t if `X' is a meeting."
+  (equal 'meeting (todo-type x)))
+
+(defun todo/label (x)
+  "Return the label of `X'."
+  (todo-label x))
+
+;; TODO: Support moving todos between todo/{today,tomorrow}.
+;; TODO: Consider modelling todo/{today,tomorrow} as queues instead of lists so that I can
+;; append cheaply.
+
+;; TODO: Find an Elisp date library.
+
+;; TODO: type-driven development of this habit tree.
+;; TODO: Create this tree on a whiteboard first.
+;; (defconst todo/habits
+;;   '(:beginning-of-month
+;;     '("Create habit template for current month"
+;;       "Post mortem of previous month")
+;;     :monday    '("Jiu Jitsu")
+;;     :tuesday   '("Jiu Jitsu")
+;;     :wednesday '("Jiu Jitsu")
+;;     :thursday  '("Salsa class")
+;;     :friday    '("Jiu Jitsu")
+;;     :saturday  '("Borough market")
+;;     :sunday    '("Shave")
+;;     :weekday '(:arrive-at-work
+;;                '("Breakfast"
+;;                  "Coffee"
+;;                  "Placeholder")
+;;                :before-lunch
+;;                '("Lock laptop"
+;;                  "Placeholder")
+;;                :home->work
+;;                '("Michel Thomas Italian lessons"))
+;;     :daily '(:morning
+;;              '("Meditate"
+;;                "Stretch")
+;;              :)))
+
+;; overlay weekday with specific weekdays (e.g. BJJ is only on M,T,W)
+
+;; TODO: Extend the record type to support duration estimations for AFK, K
+;; calculations.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Habits
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Should I be writing this in ReasonML and Haskell?
+
+(defconst todo/monthly-habit-challenge
+  "InterviewCake.com"
+  "The monthly habit challenge I do for fifteen minutes each day.")
+
+(defconst todo/daily-habits
+  (->> (list "Meditate"
+             todo/monthly-habit-challenge)
+       (-map #'todo/habit)))
+
+(defconst todo/first-of-the-month-stack
+  '("Create habit template for current month"
+    "Reserve two dinners in London for dates"
+    "Post mortem of previous month"
+    "Create monthly financial budget in Google Sheets")
+  "A stack of habits that I do at the beginning of each month.")
+
+(defconst todo/adhoc-habits
+  (->> (list/concat
+        todo/first-of-the-month-stack)
+       (-map #'todo/habit))
+  "Habits that I have no classification for at the moment.")
+
+;; TODO: Model this as a function.
+(defconst todo/habits
+  (list/concat todo/daily-habits
+               todo/adhoc-habits)
+  "My habits for today.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Meetings
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Define "meeting".
+
+(defconst todo/daily-meetings
+  (->> '("Standup"
+         "Lunch")
+       (-map #'todo/meeting))
+  "Daily, recurrent meetings.")
+
+
+(defconst todo/day-of-week-meetings
+  '(:Monday    '("Lunch")
+    :Tuesday   '("Lunch")
+    :Wednesday '("Team Lunch")
+    :Thursday  '("Lunch")
+    :Friday    '("Lunch")
+    :Satuday   '()
+    :Sunday    '())
+  "Meetings that occur depending on the current day of the week.")
+
+(parse-time-string "today")
+
+;; TODO: Support recurrent, non-daily meetings.
+
+(defconst todo/adhoc-meetings
+  (->> '("WSE Weekly Standup"
+         "Team Lunch"
+         "Karisa Explains It All")
+       (-map #'todo/meeting))
+  "Non-recurrent meetings.")
+
+(defconst todo/meetings
+  (list/concat todo/daily-meetings
+               todo/adhoc-meetings)
+  "My meetings for today.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tasks
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst todo/tasks
+  (->> '("GetEmailCase"
+         "Async node workflow"
+         "Support C-c in EXWM"
+         "Post-its for bathroom mirror"
+         "Visit AtomicHabit.com/scorecard"
+         "Visit AtomicHabit.com/habitstacking"
+         "Create GraphViz for Carpe Diem cirriculum"
+         "Create CitC client for local browsing of CE codebase"
+         "Respond to SRE emails")
+       (-map #'todo/task)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Work queues (today, tomorrow, someday)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Generate standup documents from DONE items in the state.
+
+;; TODO: Learn how to create a gen-server style of live, reactive state.
+;; TODO: This should probably be `defconst' and a reference to the live state.
+(defconst todo/today
+  (list/concat
+   todo/habits
+   todo/meetings
+   todo/tasks))
+
+(defconst todo/tomorrow
+  '())
+
+(defconst todo/someday
+  '())
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; View functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun todo/to-org (xs)
+  "Map `XS' into a string with `org-mode' syntax."
+  ;; TODO: Create function to DRY this code up.
+  (let ((meetings (->> xs
+                       (-filter #'todo/meeting?)
+                       (-map (lambda (x)
+                               (s-concat "** TODO " (todo/label x))))
+                       (s-join "\n")))
+        (tasks (->> xs
+                    (-filter #'todo/task?)
+                    (-map (lambda (x)
+                            (s-concat "** TODO " (todo/label x))))
+                    (s-join "\n")))
+        (habits (->> xs
+                     (-filter #'todo/habit?)
+                     (-map (lambda (x)
+                             (s-concat "** TODO " (todo/label x))))
+                     (s-join "\n"))))
+    (s-join "\n" (list
+                  (s-concat "* Meetings\n" meetings)
+                  (s-concat "* Tasks\n" tasks)
+                  (s-concat "* Habits\n" habits)))))
+
+(defun todo/export-to-org (xs)
+  "Export `XS' to `todo/org-file-path'."
+  (f-write-text (->> xs
+                     todo/to-org)
+                'utf-8
+                todo/org-file-path))
+
+(defun todo/orgify-today ()
+  "Exports today's todos to an org file."
+  (interactive)
+  (todo/export-to-org todo/today)
+  (alert (string/concat  "Exported today's TODOs to: " todo/org-file-path)))
+
+(when todo/install-kbds?
+  (evil-leader/set-key
+    "to" #'todo/orgify-today))
+
+(provide 'todo)
+;;; todo.el ends here
diff --git a/configs/shared/.emacs.d/wpc/tree.el b/configs/shared/.emacs.d/wpc/tree.el
new file mode 100644
index 0000000000..43df4dc500
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/tree.el
@@ -0,0 +1,193 @@
+;;; tree.el --- Working with Trees -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Some friendly functions that hopefully will make working with trees cheaper
+;; and therefore more appealing!
+;;
+;; Tree terminology:
+;; - leaf: node with zero children.
+;; - root: node with zero parents.
+;; - depth: measures a node's distance from the root node.  This implies the
+;;   root node has a depth of zero.
+;; - height: measures the longest traversal from a node to a leaf.  This implies
+;;   that a leaf node has a height of zero.
+;; - balanced?
+;;
+;; Tree variants:
+;; - binary: the maximum number of children is two.
+;; - binary search: the maximum number of children is two and left sub-trees are
+;;   lower in value than right sub-trees.
+;; - rose: the number of children is variable.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'list)
+(require 'set)
+(require 'tuple)
+(require 'series)
+(require 'random)
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct tree xs)
+
+(cl-defstruct node value children)
+
+(cl-defun tree/node (value &optional children)
+  "Create a node struct of VALUE with CHILDREN."
+  (make-node :value value
+             :children children))
+
+(defun tree/reduce-breadth (acc f xs)
+  "Reduce over XS breadth-first applying F to each x and ACC (in that order).
+Breadth-first traversals guarantee to find the shortest path in a graph.
+  They're typically more difficult to implement than DFTs and may also incur
+  higher memory costs on average than their depth-first counterparts.")
+
+;; TODO: Support :order as 'pre | 'in | 'post.
+;; TODO: Troubleshoot why I need defensive (nil? node) check.
+(defun tree/reduce-depth (acc f node)
+  "Reduce over NODE depth-first applying F to each NODE and ACC.
+F is called with each NODE, ACC, and the current depth.
+Depth-first traversals have the advantage of typically consuming less memory
+  than their breadth-first equivalents would have.  They're also typically
+  easier to implement using recursion.  This comes at the cost of not
+  guaranteeing to be able to find the shortest path in a graph."
+  (cl-labels ((do-reduce-depth
+               (acc f node depth)
+               (let ((acc-new (funcall f node acc depth)))
+                 (if (or (maybe/nil? node)
+                         (tree/leaf? node))
+                     acc-new
+                   (list/reduce
+                    acc-new
+                    (lambda (node acc)
+                      (tree/do-reduce-depth
+                       acc
+                       f
+                       node
+                       (number/inc depth)))
+                    (node-children node))))))
+    (do-reduce-depth acc f node 0)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helpers
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun tree/height (xs)
+  "Return the height of tree XS.")
+
+;; TODO: Troubleshoot why need for (nil? node).  Similar misgiving
+;; above.
+(defun tree/leaf-depths (xs)
+  "Return a list of all of the depths of the leaf nodes in XS."
+  (list/reverse
+   (tree/reduce-depth
+    '()
+    (lambda (node acc depth)
+      (if (or (maybe/nil? node)
+              (tree/leaf? node))
+          (list/cons depth acc)
+        acc))
+    xs)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Generators
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Consider parameterizing height, forced min-max branching, random
+;; distributions, etc.
+
+;; TODO: Bail out before stack overflowing by consider branching, current-depth.
+
+(cl-defun tree/random (&optional (value-fn (lambda (_) nil))
+                                 (branching-factor 2))
+  "Randomly generate a tree with BRANCHING-FACTOR using VALUE-FN to compute the
+node values.  VALUE-FN is called with the current-depth of the node.  Useful for
+generating test data.  Warning this function can overflow the stack."
+  (cl-labels ((do-random
+               (d vf bf)
+               (make-node
+                :value (funcall vf d)
+                :children (->> (series/range 0 (number/dec bf))
+                               (list/map
+                                (lambda (_)
+                                  (when (random/boolean?)
+                                    (do-random d vf bf))))))))
+    (do-random 0 value-fn branching-factor)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun tree/instance? (tree)
+  "Return t if TREE is a tree struct."
+  (node-p tree))
+
+(defun tree/leaf? (node)
+  "Return t if NODE has no children."
+  (maybe/nil? (node-children node)))
+
+(defun tree/balanced? (n xs)
+  "Return t if the tree, XS, is balanced.
+A tree is balanced if none of the differences between any two depths of two leaf
+  nodes in XS is greater than N."
+  (> n (->> xs
+            tree/leaf-depths
+            set/from-list
+            set/count
+            number/dec)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst tree/enable-testing? t
+  "When t, test suite runs.")
+
+;; TODO: Create set of macros for a proper test suite including:
+;; - describe (arbitrarily nestable)
+;; - it (arbitrarily nestable)
+;; - line numbers for errors
+;; - accumulated output for synopsis
+;; - do we want describe *and* it? Why not a generic label that works for both?
+(when tree/enable-testing?
+  (let ((tree-a (tree/node 1
+                           (list (tree/node 2
+                                            (list (tree/node 5)
+                                                  (tree/node 6)))
+                                 (tree/node 3
+                                            (list (tree/node 7)
+                                                  (tree/node 8)))
+                                 (tree/node 4
+                                            (list (tree/node 9)
+                                                  (tree/node 10))))))
+        (tree-b (tree/node 1
+                           (list (tree/node 2
+                                            (list (tree/node 5)
+                                                  (tree/node 6)))
+                                 (tree/node 3)
+                                 (tree/node 4
+                                            (list (tree/node 9)
+                                                  (tree/node 10)))))))
+    ;; instance?
+    (prelude/assert (tree/instance? tree-a))
+    (prelude/assert (tree/instance? tree-b))
+    (prelude/refute (tree/instance? '(1 2 3)))
+    (prelude/refute (tree/instance? "oak"))
+    ;; balanced?
+    (prelude/assert (tree/balanced? 1 tree-a))
+    (prelude/refute (tree/balanced? 1 tree-b))
+    (message "Tests pass!")))
+
+(provide 'tree)
+;;; tree.el ends here
diff --git a/configs/shared/.emacs.d/wpc/tuple.el b/configs/shared/.emacs.d/wpc/tuple.el
new file mode 100644
index 0000000000..ccebf7299a
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/tuple.el
@@ -0,0 +1,86 @@
+;;; tuple.el --- Tuple API for Elisp -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Work with cons cells with two elements with a familiar API for those who have
+;; worked with tuples before.
+
+;;; Code:
+
+(cl-defstruct tuple first second)
+
+;; Create
+(defun tuple/new ()
+  "Return an empty tuple."
+  (make-tuple :first nil
+              :second nil))
+
+(defun tuple/from (a b)
+  "Return a new tuple from A and B."
+  (make-tuple :first a
+              :second b))
+
+(defun tuple/from-dotted (dp)
+  "Convert dotted pair, DP, into a tuple."
+  (tuple/from (car dp) (cdr dp)))
+
+;; Read
+(defun tuple/first (pair)
+  "Return the first element of PAIR."
+  (tuple-first pair))
+
+(defun tuple/second (pair)
+  "Return the second element of PAIR."
+  (tuple-second pair))
+
+;; Update
+(defun tuple/map-each (f g pair)
+  "Apply F to first, G to second in PAIR."
+  (->> pair
+       (tuple/map-first f)
+       (tuple/map-second g)))
+
+(defun tuple/map (f pair)
+  "Apply F to PAIR."
+  (let ((pair-copy (copy-tuple pair)))
+    (funcall f pair-copy)))
+
+(defun tuple/map-first (f pair)
+  "Apply function F to the first element of PAIR."
+  (let ((pair-copy (copy-tuple pair)))
+    (setf (tuple-first pair-copy) (funcall f (tuple/first pair-copy)))
+    pair-copy))
+
+(defun tuple/map-second (f pair)
+  "Apply function F to the second element of PAIR."
+  (let ((pair-copy (copy-tuple pair)))
+    (setf (tuple-second pair-copy) (funcall f (tuple/second pair-copy)))
+    pair-copy))
+
+(defun tuple/set-first (a pair)
+  "Return a new tuple with the first element set as A in PAIR."
+  (tuple/map-first (lambda (_) a) pair))
+
+(defun tuple/set-second (b pair)
+  "Return a new tuple with the second element set as B in PAIR."
+  (tuple/map-second (lambda (_) b) pair))
+
+;; Delete
+(defun tuple/delete-first (pair)
+  "Return PAIR with the first element set to nil."
+  (tuple/set-first nil pair))
+
+(defun tuple/delete-second (pair)
+  "Return PAIR with the second element set to nil."
+  (tuple/set-second nil pair))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun tuple/instance? (x)
+  "Return t if X is a tuple."
+  (tuple-p x))
+
+(provide 'tuple)
+;;; tuple.el ends here
diff --git a/configs/shared/.emacs.d/wpc/vector.el b/configs/shared/.emacs.d/wpc/vector.el
new file mode 100644
index 0000000000..4fc6ffe911
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/vector.el
@@ -0,0 +1,59 @@
+;;; vector.el --- Working with Elisp's Vector data type -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; It might be best to think of Elisp vectors as tuples in languages like
+;; Haskell or Erlang.
+;;
+;; Not surprisingly, this API is modelled after Elixir's Tuple API.
+;;
+;; Some Elisp trivia:
+;; - "Array": Usually means vector or string.
+;; - "Sequence": Usually means list or "array" (see above).
+;;
+;; It might be a good idea to think of Array and Sequence as typeclasses in
+;; Elisp.  This is perhaps more similar to Elixir's notion of the Enum protocol.
+;;
+;; Intentionally not supporting a to-list function, because tuples can contain
+;; heterogenous types whereas lists should contain homogenous types.
+
+;;; Code:
+
+;; TODO: Consider supporting an alias named tuple for vector.
+
+;; TODO: Consider labelling variadic functions like `vector/concat*'
+;; vs. `vector/concat'.
+(defun vector/concat (&rest args)
+  "Return a new vector composed of all vectors in `ARGS'."
+  (apply #'vconcat args))
+
+;; TODO: Here's a sketch of a protocol macro being consumed.
+;; (definstance monoid vector
+;;   :empty (lambda () []))
+
+(defun vector/prepend (x xs)
+  "Add `X' to the beginning of `XS'."
+  (vector/concat `[,x] xs))
+
+(defun vector/append (x xs)
+  "Add `X' to the end of `XS'."
+  (vector/concat xs `[,x]))
+
+(defun vector/get (i xs)
+  "Return the value in `XS' at index, `I'."
+  (aref xs i))
+
+(defun vector/set (i v xs)
+  "Set index `I' to value `V' in `XS'.
+Returns a copy of `XS' with the updates."
+  (let ((copy (vconcat [] xs)))
+    (aset copy i v)
+    copy))
+
+;; TODO: Decide between "remove" and "delete" as the appropriate verbs.
+;; TODO: Implement this.
+;; (defun vector/delete (i xs)
+;;   "Remove the element at `I' in `XS'.")
+
+(provide 'vector)
+;;; vector.el ends here
diff --git a/configs/shared/.emacs.d/wpc/wallpaper.el b/configs/shared/.emacs.d/wpc/wallpaper.el
new file mode 100644
index 0000000000..63548964b7
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/wallpaper.el
@@ -0,0 +1,84 @@
+;;; wallpaper.el --- Control Linux desktop wallpaper -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Functions for setting desktop wallpaper.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'fs)
+(require 'cycle)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defcustom wallpaper/keybindings? t
+  "If non-nil, install the keybindings.")
+
+(defcustom wallpaper/path-to-dir
+  (f-expand "~/.local/share/wallpaper")
+  "Path to the images that will be used as the wallpaper.")
+
+(defconst wallpaper/whitelist
+  (cycle/from-list
+   (fs/ls wallpaper/path-to-dir t))
+  "My preferred computer wallpapers.")
+
+(defun wallpaper/set (path)
+  "Set computer wallpaper to image at `PATH' using `feh` under-the-hood."
+  (shell-command (string/format "feh --bg-scale --no-fehbg %s" path)))
+
+(defun wallpaper/whitelist-set (wallpaper)
+  "Focuses the WALLPAPER in the `wallpaper/whitelist' cycle."
+  (cycle/focus (lambda (x) (equal x wallpaper)) wallpaper/whitelist)
+  (wallpaper/set (wallpaper/current)))
+
+(defun wallpaper/next ()
+  "Cycles to the next wallpaper."
+  (interactive)
+  (let ((wallpaper (cycle/next wallpaper/whitelist)))
+    (wallpaper/set wallpaper)
+    (message (string/format "Active wallpaper: %s" (f-filename wallpaper)))))
+
+(defun wallpaper/prev ()
+  "Cycles to the previous wallpaper."
+  (interactive)
+  (let ((wallpaper (cycle/prev wallpaper/whitelist)))
+    (wallpaper/set wallpaper)
+    (message (string/format "Active wallpaper: %s" (f-filename wallpaper)))))
+
+;; TODO: Define a macro that handles, next, prev, select, current for working
+;; with cycles, since this is a common pattern.
+
+(defun wallpaper/print-current ()
+  "Message the currently enabled wallpaper."
+  (interactive)
+  (message
+   (cycle/current wallpaper/whitelist)))
+
+(defun wallpaper/current ()
+  "Return the currently enabled wallpaper."
+  (cycle/current wallpaper/whitelist))
+
+(defun wallpaper/ivy-select ()
+  "Use `counsel' to select and set a wallpaper from the `wallpaper/whitelist'."
+  (interactive)
+  (wallpaper/whitelist-set
+   (ivy-read "Select wallpaper: " (cycle/to-list wallpaper/whitelist))))
+
+;; TODO: Create macro-based module system that will auto-namespace functions,
+;; constants, etc. with the filename like `wallpaper'.
+
+(when wallpaper/keybindings?
+  (evil-leader/set-key
+    "Fw" #'wallpaper/next
+    "Pw" #'wallpaper/prev))
+
+(provide 'wallpaper)
+;;; wallpaper.el ends here
diff --git a/configs/shared/.emacs.d/wpc/window-manager.el b/configs/shared/.emacs.d/wpc/window-manager.el
new file mode 100644
index 0000000000..76586303dc
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/window-manager.el
@@ -0,0 +1,672 @@
+;;; window-manager.el --- Functions to ease my transition to EXWM. -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; It's possible that this module will be entirely temporary.  Creating it after
+;; switching to EXWM to help transfer my reliance from i3 to EXWM.
+;;
+;; Wish list:
+;; - TODO: Support different startup commands and layouts depending on laptop or
+;;   desktop.
+;; - TODO: Support a Music named-workspace.
+
+;;; Code:
+
+(require 'prelude)
+(require 'string)
+(require 'cycle)
+(require 'set)
+(require 'kbd)
+(require 'ivy-helpers)
+(require 'display)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Associate `window-purpose' window-layouts with each of these named
+;; workspaces.
+
+;; TODO: Associate KBDs for each of these named-layouts.
+
+;; TODO: Decide between window-manager, exwm, or some other namespace.
+
+;; TODO: Support (cycle/from-list '(current previous)) to toggle back and forth
+;; between most recent workspace.
+
+;; TODO: Support ad hoc cycle for loading a few workspaces that can be cycled
+;; between. (cycle/from-list '("Project" "Workspace"))
+
+;; TODO: Consider supporting a workspace for Racket, Clojure, Common Lisp,
+;; Haskell, Elixir, and a few other languages. These could behave very similarly
+;; to repl.it, which I've wanted to have locally for awhile now.
+
+;; TODO: Support MRU cache of workspaces for easily switching back-and-forth
+;; between workspaces.
+
+(cl-defstruct exwm/named-workspace
+  label
+  index
+  kbd)
+
+(defconst exwm/install-workspace-kbds? t
+  "When t, install the keybindings to switch between named-workspaces.")
+
+;; TODO: Consume `cache/touch' after changing workspaces.  Use this to enable
+;; cycling through workspaces.
+
+(defconst exwm/named-workspaces
+  (list (make-exwm/named-workspace
+         :label "Web surfing"
+         :index 0
+         :kbd "c")
+        (make-exwm/named-workspace
+         :label "Project"
+         :index 1
+         :kbd "p")
+        (make-exwm/named-workspace
+         :label "Dotfiles"
+         :index 2
+         :kbd "d")
+        (make-exwm/named-workspace
+         :label "Scratch"
+         :index 3
+         :kbd "s")
+        (make-exwm/named-workspace
+         :label "Terminal"
+         :index 4
+         :kbd "t")
+        (make-exwm/named-workspace
+         :label "Todos"
+         :index 5
+         :kbd "o")
+        (make-exwm/named-workspace
+         :label "Chatter"
+         :index 6
+         :kbd "h")
+        (make-exwm/named-workspace
+         :label "Work"
+         :index 7
+         :kbd "w"))
+  "List of `exwm/named-workspace' structs.")
+
+;; Assert that no two workspaces share KBDs.
+(prelude/assert (= (list/length exwm/named-workspaces)
+                   (->> exwm/named-workspaces
+                        (list/map #'exwm/named-workspace-kbd)
+                        set/from-list
+                        set/count)))
+
+(defun window-manager/alert (x)
+  "Message X with a structured format."
+  (alert (string/concat "[exwm] " x)))
+
+;; Use Emacs as my primary window manager.
+(use-package exwm
+  :config
+  (require 'exwm-config)
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+  ;; Multiple Displays
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+  (require 'exwm-randr)
+  (exwm-randr-enable)
+  ;; TODO: Consider generating this plist.
+  ;; TODO: Replace integer index values with their named workspace equivalents.
+  (setq exwm-randr-workspace-monitor-plist (list 0 display/4k
+                                                 1 display/primary))
+
+  (evil-set-initial-state 'exwm-mode 'emacs)
+  (ido-mode 1)
+  (exwm-config-ido)
+  (setq exwm-workspace-number
+        (list/length exwm/named-workspaces))
+  ;; EXWM supports "line-mode" and "char-mode".
+  ;;
+  ;; Note: It appears that calling `exwm-input-set-key' works if it's called
+  ;; during startup.  Once a session has started, it seems like this function is
+  ;; significantly less useful.  Is this a bug?
+  ;;
+  ;; Glossary:
+  ;; - char-mode: All keystrokes except `exwm' global ones are passed to the
+  ;;   application.
+  ;; - line-mode:
+  ;;
+  ;; `exwm-input-global-keys' = {line,char}-mode; can also call `exwm-input-set-key'
+  ;; `exwm-mode-map'          = line-mode
+  ;; `???'                    = char-mode. Is there a mode-map for this?
+  ;;
+  ;; TODO: What is `exwm-input-prefix-keys'?
+  ;; TODO: Once I get `exwm-input-global-keys' functions, drop support for
+  ;; `wpc/kbds'.
+  (let ((kbds `(
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; Window sizing
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                (:key "C-M-=" :fn balance-windows)
+                ;; TODO: Make sure these don't interfere with LISP KBDs.
+                (:key "C-M-j" :fn shrink-window)
+                (:key "C-M-k" :fn enlarge-window)
+                (:key "C-M-h" :fn shrink-window-horizontally)
+                (:key "C-M-l" :fn enlarge-window-horizontally)
+
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; Window traversing
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                (:key "M-h" :fn windmove-left)
+                (:key "M-j" :fn windmove-down)
+                (:key "M-k" :fn windmove-up)
+                (:key "M-l" :fn windmove-right)
+
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; Window splitting
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                (:key "M-\\" :fn evil-window-vsplit)
+                (:key "M--"  :fn evil-window-split)
+
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; Window deletion
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                (:key "M-q" :fn delete-window)
+
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; Miscellaneous
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                (:key "M-:"               :fn eval-expression)
+                (:key "M-SPC"             :fn window-manager/apps)
+                (:key "M-x"               :fn counsel-M-x)
+                (:key "<M-tab>"           :fn exwm/next-workspace)
+                (:key "<M-S-iso-lefttab>" :fn exwm/prev-workspace)
+                (:key "<M-iso-lefttab>"   :fn exwm/prev-workspace)
+                ;; <M-escape> doesn't work in X11 windows.
+                (:key "<M-escape>"        :fn exwm/ivy-switch)
+                (:key "C-M-\\"            :fn ivy-pass)
+
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; REPLs
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                (:key ,(kbd/raw 'x11 "r") :fn exwm/ivy-find-or-create-repl)
+
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ;; Workspaces
+                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+                ;; NOTE: Here I need to generate lowercase and uppercase
+                ;; variants of each because my Ergodox is sending capitalized
+                ;; variants of the keycodes to EXWM.
+                (:key ,(kbd/raw 'workspace "l") :fn window-manager/logout)
+                (:key ,(kbd/raw 'workspace "L") :fn window-manager/logout)
+                (:key ,(kbd/raw 'workspace "i") :fn exwm/toggle-mode)
+                (:key ,(kbd/raw 'workspace "I") :fn exwm/toggle-mode))))
+    (setq exwm-input-global-keys
+          (->> kbds
+               (-map (lambda (plist)
+                       `(,(kbd (plist-get plist :key)) . ,(plist-get plist :fn)))))))
+  (setq exwm-input-simulation-keys
+        ;; TODO: Consider supporting M-d and other readline style KBDs.
+        '(([?\C-b] . [left])
+          ([?\M-b] . [C-left])
+          ([?\C-f] . [right])
+          ([?\M-f] . [C-right])
+          ([?\C-p] . [up])
+          ([?\C-n] . [down])
+          ([?\C-a] . [home])
+          ([?\C-e] . [end])
+          ([?\C-d] . [delete])
+          ;; TODO: Assess whether or not this is a good idea.
+          ;; TODO: Ensure C-c copies.
+          ([?\C-c] . [C-c])))
+  (exwm-enable))
+
+;; TODO: Package workspace management in another module.
+
+;; Here is the code required to allow EXWM to cycle workspaces.
+(defconst exwm/workspaces
+  (->> exwm/named-workspaces
+       cycle/from-list)
+  "Cycle of the my EXWM workspaces.")
+
+(prelude/assert
+ (= exwm-workspace-number
+    (list/length exwm/named-workspaces)))
+
+(defun exwm/next-workspace ()
+  "Cycle forwards to the next workspace."
+  (interactive)
+  (exwm-workspace-switch
+   (exwm/named-workspace-index (cycle/next exwm/workspaces)))
+  (window-manager/alert
+   (string/concat
+    "Current workspace: "
+    (exwm/named-workspace-label (cycle/current exwm/workspaces)))))
+
+(defun exwm/prev-workspace ()
+  "Cycle backwards to the previous workspace."
+  (interactive)
+  (exwm-workspace-switch
+   (exwm/named-workspace-index (cycle/prev exwm/workspaces)))
+  (window-manager/alert
+   (string/concat
+    "Current workspace: "
+    (exwm/named-workspace-label (cycle/current exwm/workspaces)))))
+
+;; TODO: Create friendlier API for working with EXWM.
+
+;; Here is the code required to toggle EXWM's modes.
+(defun exwm/line-mode ()
+  "Switch exwm to line-mode."
+  (call-interactively #'exwm-input-grab-keyboard)
+  (window-manager/alert "Switched to line-mode"))
+
+(defun exwm/char-mode ()
+  "Switch exwm to char-mode."
+  (call-interactively #'exwm-input-release-keyboard)
+  (window-manager/alert "Switched to char-mode"))
+
+(defconst exwm/modes
+  (cycle/from-list (list #'exwm/char-mode
+                         #'exwm/line-mode))
+  "Functions to switch exwm modes.")
+
+(defun exwm/toggle-mode ()
+  "Switch between line- and char- mode."
+  (interactive)
+  (with-current-buffer (window-buffer)
+    (when (eq major-mode 'exwm-mode)
+      (funcall (cycle/next exwm/modes)))))
+
+;; Ensure exwm apps open in char-mode.
+(add-hook
+ 'exwm-manage-finish-hook
+ #'exwm/char-mode)
+
+;; Interface to the Linux password manager
+(use-package ivy-pass)
+
+(defconst exwm/preferred-terminal "terminator"
+  "My preferred terminal.")
+
+(defconst exwm/preferred-browser "google-chrome"
+  "My preferred web browser.")
+
+(defun exwm/browser-open (&optional url)
+  "Opens the URL in `exwm/preferred-browser'."
+  (exwm/open
+   (string/format "%s %s" exwm/preferred-browser url)
+   :buffer-name (string/format "*%s*<%s>" exwm/preferred-browser url)
+   :process-name url))
+
+;; TODO: Consider storing local state of all processes started with this command
+;; for some nice ways to cycle through existing terminals, etc.
+(defun exwm/terminal-open (cmd)
+  "Call CMD using `exwm/preferred-terminal'."
+  (exwm/open (string/format
+              "%s --command '%s'"
+              exwm/preferred-terminal
+              cmd)
+             :buffer-name (string/format "*%s*<%s>" exwm/preferred-terminal cmd)
+             :process-name cmd))
+
+;; TODO: Create a KBD that calls the `C-x b<Enter>' I call often.
+;; TODO: Consider auto-generating KBDs for spawning these using the first
+;; character in their name.  Also assert that none of the generated keybindings
+;; will clash with one another.
+(defconst exwm/repls
+  '(("python" . (lambda () (exwm/terminal-open "python3")))
+    ("zsh"    . (lambda () (exwm/terminal-open "zsh")))
+    ("fish"   . (lambda () (exwm/terminal-open "fish")))
+    ("nix"    . (lambda () (exwm/terminal-open "nix repl")))
+    ("racket" . racket-repl)
+    ;; NOTE: `ielm' as-is is a find-or-create operation.
+    ("elisp"  . ielm))
+  "Mapping of REPL labels to the commands needed to initialize those REPLs.")
+
+;; NOTE: Some of these commands split the window already.  Some of these
+;; commands find-or-create already.
+;;
+;; Find-or-create:
+;;        +---+---+
+;;        | Y | N |
+;;        +---+---+
+;; python |   | x |
+;; zsh    |   | x |
+;; racket | x |   |
+;; elisp  | x |   |
+;;        +---+---+
+;;
+;; Split:
+;;        +---+---+
+;;        | Y | N |
+;;        +---+---+
+;; python |   | x |
+;; zsh    |   | x |
+;; racket | x |   |
+;; elisp  |   | x |
+;;        +---+---+
+
+;; - Split:
+;;   - racket
+(defun exwm/ivy-find-or-create-repl ()
+  "Select a type of REPL using `ivy' and then find-or-create it."
+  (interactive)
+  (ivy-helpers/kv "REPLs: "
+                  exwm/repls
+                  (lambda (_ v)
+                    (funcall v))))
+
+;; KBDs to quickly open X11 applications.
+(general-define-key
+ ;; TODO: Eventually switch this to a find-or-create operation.  In general, I
+ ;; shouldn't need multiple instances of `python3` REPLs.
+ ;; TODO: Consider coupling these KBDs with the `exwm/ivy-find-or-create-repl'
+ ;; functionality defined above.
+ (kbd/raw 'x11 "n") (lambda ()
+                      (interactive)
+                      (exwm/terminal-open "nix repl"))
+ (kbd/raw 'x11 "p") (lambda ()
+                      (interactive)
+                      (exwm/terminal-open "python3"))
+ (kbd/raw 'x11 "t") (lambda ()
+                      (interactive)
+                      (exwm/terminal-open "zsh"))
+ (kbd/raw 'x11 "c") (lambda ()
+                      (interactive)
+                      (exwm/open exwm/preferred-browser)))
+
+;; TODO: Support searching all "launchable" applications like OSX's Spotlight.
+;; TODO: Model this as key-value pairs.
+(defconst window-manager/applications
+  (list "google-chrome --new-window --app=https://chat.google.com"
+        "google-chrome --new-window --app=https://calendar.google.com"
+        "google-chrome --new-window --app=https://gmail.com"
+        "google-chrome --new-window --app=https://web.telegram.org"
+        "google-chrome --new-window --app=https://teknql.slack.com"
+        "google-chrome --new-window --app=https://web.whatsapp.com"
+        "google-chrome --new-window --app=https://irccloud.com"
+        exwm/preferred-browser
+        exwm/preferred-terminal)
+  "Applications that I commonly use.
+These are the types of items that would usually appear in dmenu.")
+
+;; TODO: Consider replacing the `ivy-read' call with something like `hydra' that
+;; can provide a small mode for accepting user-input.
+;; TODO: Put this somewhere more diliberate.
+
+(defun window-manager/screenshot ()
+  "Choose between \"Local\" and \"Google\" screenshots."
+  (interactive)
+  (pcase (ivy-read "Type of screenshot: " '("Google" "Local"))
+    ;; TODO: Drop `zsh -i -c' dependency and reimplement in Elisp.
+    ("Google" (shell-command "zsh -i -c snipit"))
+    ("Local"  (shell-command "zsh -i -c screenshot"))))
+
+;; TODO: Configure the environment variables for xsecurelock so that the font is
+;; smaller, different, and the glinux wallpaper doesn't show.
+;; - XSECURELOCK_FONT="InputMono-Black 10"
+;; - XSECURE_SAVER=""
+;; - XSECURE_LOGO_IMAGE=""
+;; Maybe just create a ~/.xsecurelockrc
+;; TODO: Is there a shell-command API that accepts an alist and serializes it
+;; into variables to pass to the shell command?
+(defconst window-manager/xsecurelock
+  "/usr/share/goobuntu-desktop-files/xsecurelock.sh"
+  "Path to the proper xsecurelock executable.
+The other path to xsecurelock is /usr/bin/xsecurelock, which works fine, but it
+is not optimized for Goobuntu devices.  Goobuntu attempts to check a user's
+password using the network.  When there is no network connection available, the
+login attempts fail with an \"unknown error\", which isn't very helpful.  To
+avoid this, prefer the goobuntu wrapper around xsecurelock when on a goobuntu
+device.  This all relates to PAM (i.e. pluggable authentication modules).")
+
+(defun window-manager/logout ()
+  "Prompt the user for options for logging out, shutting down, etc.
+
+The following options are supported:
+- Lock
+- Logout
+- Suspend
+- Hibernate
+- Reboot
+- Shutdown
+
+Ivy is used to capture the user's input."
+  (interactive)
+  (let* ((name->cmd `(("Lock" . ,window-manager/xsecurelock)
+                      ("Logout" . "sudo systemctl stop lightdm")
+                      ("Suspend" . ,(string/concat
+                                     window-manager/xsecurelock
+                                     " && systemctl suspend"))
+                      ("Hibernate" . ,(string/concat
+                                       window-manager/xsecurelock
+                                       " && systemctl hibernate"))
+                      ("Reboot" . "systemctl reboot")
+                      ("Shutdown" . "systemctl poweroff"))))
+    (funcall
+     (lambda ()
+       (shell-command
+        (alist/get (ivy-read "System: " (alist/keys name->cmd))
+                   name->cmd))))))
+
+(cl-defun exwm/open (command &key
+                             (process-name command)
+                             (buffer-name command))
+  "Open COMMAND, which should be an X11 window."
+  (start-process-shell-command process-name buffer-name command))
+
+(cl-defun window-manager/execute-from-counsel (&key prompt list)
+  "Display a counsel menu of `LIST' with `PROMPT' and pipe the output through
+`start-process-shell-command'."
+  (let ((x (ivy-read prompt list)))
+    (exwm/open
+     x
+     :buffer-name (string/format "*exwm/open*<%s>" x)
+     :process-name x)))
+
+(defun window-manager/apps ()
+  "Open commonly used applications from counsel."
+  (interactive)
+  (window-manager/execute-from-counsel
+   :prompt "Application: "
+   :list window-manager/applications))
+
+;; TODO: KBDs for {enlarge,shrink}-window
+
+(use-package window-purpose
+  :config
+  (purpose-mode 1)
+
+  ;; call `purpose-compile-user-configuration' after making changes.
+
+  ;; Example configuration:
+  ;; (setq purpose-user-mode-purposes
+  ;;       '((term-mode . terminal)
+  ;;         (shell-mode . terminal)
+  ;;         (ansi-term-mode . terminal)
+  ;;         (tuareg-mode . coding)
+  ;;         (compilation-mode . messages)))
+
+  (setq purpose-user-mode-purposes
+        '(
+          ;; Overview:
+          ;; - coding: buffers for editing code
+          ;; - repl: interactive REPLs -- including terminals
+          ;; - documentation: help buffers and language documentation
+          ;;
+          ;; Ideas:
+          ;; - racket and racket-project and two different variants where the
+          ;;   project version includes a file browser.
+          ;; - google3 workspace
+
+          ;; Racket
+          (racket-mode . coding)
+          (racket-repl-mode . repl)
+          (racket-describe-mode . documentation)
+          ;; Elisp
+          (emacs-lisp-mode . coding)
+          (ielm-mode . repl)
+          ;; Python
+          ;; (python-mode . coding)
+          ;; (emacs-lisp-mode . coding)
+          ))
+
+  (macros/comment
+   (purpose-compile-user-configuration))
+
+  ;; (setq purpose-user-name-purposes
+  ;;       `(("name" . "purpose")
+  ;;         ("name" . "purpose")))
+
+  ;; (setq purpose-user-regexp-purposes
+  ;;       `(("regexp" . "purpose")
+  ;;         ("regexp" . "purpose")))
+
+  )
+
+(defun exwm/label->index (label workspaces)
+  "Return the index of the workspace in WORKSPACES named LABEL."
+  (let ((workspace (->> workspaces
+                        (list/find
+                         (lambda (x)
+                           (equal label
+                                  (exwm/named-workspace-label x)))))))
+    (if (prelude/set? workspace)
+        (exwm/named-workspace-index workspace)
+      (error (string/concat "No workspace found for label: " label)))))
+
+(defun exwm/register-kbd (workspace)
+  "Registers a keybinding for WORKSPACE struct.
+Currently using super- as the prefix for switching workspaces."
+  (let ((handler (lambda ()
+                   (interactive)
+                   (exwm/switch (exwm/named-workspace-label workspace))))
+        (key (exwm/named-workspace-kbd workspace)))
+    (exwm-input-set-key
+     (kbd/for 'workspace key)
+     handler)
+    ;; Note: We need to capitalize the KBD here because of the signals that my
+    ;; Ergodox is sending Emacs on my desktop.
+    (exwm-input-set-key
+     (kbd/for 'workspace (s-capitalize key))
+     handler)))
+
+(defun exwm/switch (label)
+  "Switch to a named workspaces using LABEL."
+  (cycle/focus
+   (lambda (x)
+     (equal label
+            (exwm/named-workspace-label x)))
+   exwm/workspaces)
+  (exwm-workspace-switch
+   (exwm/named-workspace-index (cycle/current exwm/workspaces)))
+  (window-manager/alert
+   (string/concat "Switched to: " label)))
+
+(defun exwm/ivy-switch ()
+  "Use ivy to switched between named workspaces."
+  (interactive)
+  (ivy-read
+   "Workspace: "
+   (->> exwm/named-workspaces
+        (list/map #'exwm/named-workspace-label))
+   :action #'exwm/switch))
+
+(when exwm/install-workspace-kbds?
+  (progn
+    (->> exwm/named-workspaces
+         (list/map #'exwm/register-kbd))
+    (window-manager/alert "Registered workspace KBDs!")))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Startup Applications in `exwm/named-workspaces'
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(add-hook
+ 'exwm-init-hook
+ (lambda ()
+   ;; TODO: Refactor this into a bigger solution where the named-workspaces are
+   ;; coupled to their startup commands.  Expedience wins this time.
+   (progn
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Chrome
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Web surfing")
+       ;; make sure this blocks.
+       ;; TODO: Support shell-cmd.el that has `shell-cmd/{sync,async}'.
+       ;; (call-process-shell-command "google-chrome")
+       )
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Project
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Project")
+       (find-file constants/current-project))
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Scratch
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Scratch")
+       (switch-to-buffer "*scratch*"))
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Terminal
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Terminal")
+       ;; TODO: Why does "gnome-terminal" work but not "terminator"?
+       ;; (call-process-shell-command "gnome-terminal")
+       )
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Todos
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Todos")
+       (find-file "~/Dropbox/org/today.org")
+       (wpc/evil-window-vsplit-right)
+       (find-file "~/Dropbox/org/emacs.org"))
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Dotfiles
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Dotfiles")
+       ;; TODO: Support (dotfiles/find-file "window-manager.el")?
+       (find-file "~/Dropbox/dotfiles/configs/shared/.emacs.d/init.el")
+       (wpc/evil-window-vsplit-right)
+       (find-file
+        "~/Dropbox/dotfiles/configs/shared/.emacs.d/wpc/window-manager.el"))
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Chatter
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Chatter")
+       ;; TODO: Support the following chat applications:
+       ;; - Slack teknql
+       ;; - irccloud.net
+       ;; - web.whatsapp.com
+       ;; - Telegram
+       ;; NOTE: Perhaps all of these should be borderless.
+       ;; (call-process-shell-command "terminator")
+       )
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Work
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (progn
+       (exwm/switch "Work")
+       ;; TODO: Support opening the following in chrome:
+       ;; - calendar
+       ;; - gmail
+       ;; - chat (in a horizontal split)
+       )
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     ;; Reset to default
+     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+     (exwm/switch "Dotfiles")
+     )))
+
+(provide 'window-manager)
+;;; window-manager.el ends here
diff --git a/configs/shared/.emacs.d/wpc/window.el b/configs/shared/.emacs.d/wpc/window.el
new file mode 100644
index 0000000000..132156bc44
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/window.el
@@ -0,0 +1,37 @@
+;;; window.el --- Working with Emacs windows -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Utilities to make CRUDing windows in Emacs easier.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'macros)
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun window/find (name)
+  "Find a window by the NAME of the buffer it's hosting."
+  (let ((buffer (get-buffer name)))
+    (if (maybe/some? buffer)
+        (get-buffer-window buffer)
+      nil)))
+
+;; TODO: Find a way to incorporate these into function documentation.
+(macros/comment
+ (window/find "*scratch*"))
+
+(defun window/delete (window)
+  "Delete the WINDOW reference."
+  (delete-window window))
+
+(provide 'window)
+;;; window.el ends here
diff --git a/configs/shared/.emacs.d/wpc/wpgtk.el b/configs/shared/.emacs.d/wpc/wpgtk.el
new file mode 100644
index 0000000000..432d828843
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/wpgtk.el
@@ -0,0 +1,45 @@
+;; wpgtk.el -- A base16 colorscheme template for wpgtk.
+
+;;; Commentary:
+
+;;; Authors:
+;; Template: William Carroll <wpcarro@gmail.com>
+
+;;; Code:
+
+(require 'base16-theme)
+(require 'colorscheme)
+
+(defvar base16-wpgtk-colors
+  '(:base00 "#31213f"
+    :base01 "#E29B61"
+    :base02 "#E8C35F"
+    :base03 "#565B87"
+    :base04 "#A56785"
+    :base05 "#20A89E"
+    :base06 "#3CC2B5"
+    :base07 "#8de0e1"
+    :base08 "#629c9d"
+    :base09 "#E29B61"
+    :base0A "#E8C35F"
+    :base0B "#565B87"
+    :base0C "#A56785"
+    :base0D "#20A89E"
+    :base0E "#3CC2B5"
+    :base0F "#8de0e1")
+  "All colors for Base16 wpgtk are defined here.")
+
+;; Define the theme
+(deftheme base16-wpgtk)
+
+;; Add all the faces to the theme
+(base16-theme-define 'base16-wpgtk base16-wpgtk-colors)
+
+;; Mark the theme as provided
+(provide-theme 'base16-wpgtk)
+
+(macros/comment
+ (colorscheme/set 'base16-wpgtk))
+
+(provide 'wpgtk)
+;;; wpgtk.el ends here
diff --git a/configs/shared/.emacs.d/wpc/zle.el b/configs/shared/.emacs.d/wpc/zle.el
new file mode 100644
index 0000000000..1b01da9384
--- /dev/null
+++ b/configs/shared/.emacs.d/wpc/zle.el
@@ -0,0 +1,90 @@
+;;; zle.el --- Functions to mimmick my ZLE KBDs -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; This is primarily for personal use.  The keybindings that I choose are those
+;; that feel slightly mnemonic while also not shadowing important bindings.
+;; It's quite possible that our tastes will differ here.
+;;
+;; All of these keybindings are intended to shave off milliseconds off your
+;; typing.  I don't expect these numbers to sum up to a meaningful amount.  The
+;; primary reason that I wrote this, is that it introduces a small amount of
+;; structural editing to my workflow.  I've been using these exact keybindings
+;; on the command line, and I find them subtely delightful to use.  So much so
+;; that I decided to bring them to my Emacs configuration.
+;;
+;; ZLE is the Z-shell line editor.  I have some KBDs and functions that I often
+;; want in Emacs.
+;;
+;; Usage:
+;; Consider running `(zle-minor-mode)' to run this globally.  Depending on your
+;; configuration, it could be non-disruptive, disruptive, or extremely
+;; disruptive.
+;;
+;; TODO: Consider adding (general-unbind 'insert "C-v") herein.
+
+;;; Code:
+
+;; subshell (C-j)
+(defun zle/subshell ()
+  "Insert the characters necessary to create a subshell."
+  (interactive)
+  (insert-char ?$)
+  (insert-char ?\()
+  (save-excursion
+    (insert-char ?\))))
+
+;; variable (C-v)
+(defun zle/variable ()
+  "Insert the characters to reference a variable."
+  (interactive)
+  (insert-char ?$)
+  (insert-char ?{)
+  (save-excursion
+    (insert-char ?})))
+
+;; 2x dash (C-M--)
+(defun zle/dash-dash ()
+  "Insert the characters for flags with 2x dashes."
+  (interactive)
+  (insert-char ? )
+  (insert-char ?-)
+  (insert-char ?-))
+
+;; 1x quotes (M-')
+(defun zle/single-quote ()
+  "Insert the characters to quickly create single quotes."
+  (interactive)
+  (insert-char ? )
+  (insert-char ?')
+  (save-excursion
+    (insert-char ?')))
+
+;; 2x quotes (M-")
+(defun zle/double-quote ()
+  "Insert the characters to quickly create double quotes."
+  (interactive)
+  (insert-char ? )
+  (insert-char ?\")
+  (save-excursion
+    (insert-char ?\")))
+
+(defvar zle/kbds
+  (let ((map (make-sparse-keymap)))
+    (bind-keys :map map
+               ("C-j"   . zle/subshell)
+               ("C-v"   . zle/variable)
+               ("C-M--" . zle/dash-dash)
+               ("M-'"   . zle/single-quote)
+               ("M-\""  . zle/double-quote))
+    map)
+  "Keybindings shaving milliseconds off of typing.")
+
+(define-minor-mode zle-minor-mode
+  "A minor mode mirroring my ZLE keybindings."
+  :init-value nil
+  :lighter " zle"
+  :keymap zle/kbds)
+
+(provide 'zle)
+;;; zle.el ends here