summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob L. Kreuze2020-06-01 16:53:50 -0400
committerJakob L. Kreuze2020-06-01 16:53:50 -0400
commit6c7f0b8ae2de30a808901b16b0d462fe833ea1b4 (patch)
tree2cc0a3e9022fc43c39e5b91f4e7832b5387636e9
parent4d4f759881e310818055c31f286158da9db60d1c (diff)
Break config into files.
-rw-r--r--init.el2000
-rw-r--r--lisp/appearance.el85
-rw-r--r--lisp/appearance/modeline.el87
-rw-r--r--lisp/appearance/theme.el251
-rw-r--r--lisp/core.el196
-rw-r--r--lisp/documents.el333
-rw-r--r--lisp/irc.el214
-rw-r--r--lisp/jakob.el81
-rw-r--r--lisp/languages/javascript.el23
-rw-r--r--lisp/news.el44
-rw-r--r--lisp/programming.el157
-rw-r--r--lisp/programming/c++.el29
-rw-r--r--lisp/programming/c.el42
-rw-r--r--lisp/programming/common-lisp.el32
-rw-r--r--lisp/programming/elisp.el30
-rw-r--r--lisp/programming/javascript.el26
-rw-r--r--lisp/programming/python.el24
-rw-r--r--lisp/programming/rust.el40
-rw-r--r--lisp/programming/scheme.el21
-rw-r--r--lisp/quality-of-life.el86
-rw-r--r--lisp/system.el68
-rw-r--r--lisp/tunes.el20
-rw-r--r--lisp/window-manager.el183
23 files changed, 2114 insertions, 1958 deletions
diff --git a/init.el b/init.el
index d715d5e..6c9d617 100644
--- a/init.el
+++ b/init.el
@@ -92,1967 +92,51 @@
;;; TODO
-;; Bind some keys for working with this configuration file.
-
-(defun config-goto ()
- "Switch to a buffer visiting the config file."
- (interactive)
- (find-file "~/.emacs.d/init.el"))
-
-(bind-key "C-c e" #'config-goto)
-
-;;;
-;;; The Core of GNU Emacs
-;;;
-
-;; [HACK] Use the correct `emacsclient' binary on Gentoo.
-(when (cl-search "28" (emacs-version))
- (setq with-editor-emacsclient-executable "emacsclient-emacs-28-vcs"))
-
-;; Having Emacs open to `*scratch*' feels as though it's inviting me to punch
-;; out some Lisp forms and evaluate them. I like to pretend that my computers
-;; are Lisp machines.
-
-;; TODO: Replace with dashboard.el?
-
-(setq inhibit-startup-message t
- inhibit-startup-buffer-menu t
- initial-scratch-message nil
- initial-buffer-choice nil)
-
-;; A recurring theme in this configuration file is changing where Emacs packages
-;; drop their various state files. 'no-littering.el' is a package that handles
-;; this by creating 'var/' and 'etc/' directories in the Emacs directory, but
-;; it's implemented in a way that pollutes the global namespace with variables
-;; for packages that I don't use, and would require a significant amount of
-;; configuration on my part anyway, so I've decided to do this manually, setting
-;; relevant variables to subdirectories of XDG_CACHE_HOME and XDG_DATA_HOME.
-
-;; Save backup files to one directory instead of making a mess of the filesystem.
-
-(setq backup-directory-alist `(("." . "~/.cache/emacs")))
-
-;; `auto-save', in addition to the actual files it saves, maintains another file
-;; listing the files it's currently taking care of.
-
-(setq auto-save-list-file-prefix "~/.cache/emacs/auto-save-list/.saves-")
-
-;; Disable the creation of locking symlinks in the current directory. This is a
-;; very opinionated choice, and probably isn't a good idea for most.
-
-(setq create-lockfiles nil)
-
-;; I don't use 'custom.el' to set variables, but a few of the packages I use do.
-;; This snippet ensures that a massacre is not made of my init.el.
-
-(setq custom-file "~/.emacs.d/custom.el")
-(when (file-exists-p custom-file)
- (load custom-file))
-
-;; I don't really care about persisting minibuffer history between sessions.
-;; Even if I disable `savehist-mode', it still writes to disk, so the least I
-;; can do is move the history file to somewhere that isn't .emacs.d.
-
-(savehist-mode -1)
-(setq savehist-file "~/.cache/emacs/history")
-
-(use-package tramp
- :straight nil
- :init
- (setq tramp-backup-directory-alist backup-directory-alist)
- (setq-default tramp-persistency-file-name "~/.cache/emacs/tramp"))
-
-(use-package nsm
- :straight nil
- :init
- (setq nsm-settings-file "~/.cache/emacs/network-security.data"))
-
-;; Use a consistent confirmation dialog of "y or n".
-
-(fset 'yes-or-no-p 'y-or-n-p)
-
-;; Automatically revert buffers and dired listings when something on disk
-;; changes.
-
-(use-package autorevert
- :config
- (global-auto-revert-mode 1)
- (setq global-auto-revert-non-file-buffers t
- auto-revert-verbose nil))
-
-;; "Command attempted to use minibuffer while in minibuffer" gets old fast.
-
-(setq enable-recursive-minibuffers t)
-
-;; Select help windows when I pop them so that I can kill them with <q>.
-
-(setq help-window-select t)
-
-;; Most *NIX tools work best when files are terminated with a newline.
-
-(setq require-final-newline t)
-
-;; Sentences should be separated by a single space. Treat two sentences as such
-;; when filling.
-
-(setq sentence-end-double-space nil)
-
-;; I use the tab key, but I generally prefer space characters to tabstops and
-;; like a four-character width for indentation.
-
-(setq-default indent-tabs-mode nil
- tab-width 4)
-
-;; Modern conventions state that 80 characters is the standard width.
-
-(setq-default fill-column 80)
-
-;; When killing, add whatever's in the system clipboard if it isn't there
-;; already, so I can get it back with <M-y>.
-
-(setq save-interprogram-paste-before-kill t)
-
-;; Enable the default-disabled functions that I use.
-
-(put 'downcase-region 'disabled nil)
-(put 'upcase-region 'disabled nil)
-
-;; Enable useful visual queues.
-
-(column-number-mode t)
-
-;; Unbind <C-z> and <C-x C-z> so that I'm not accidentally calling =suspend-frame=.
-
-(global-unset-key (kbd "C-z"))
-(global-unset-key (kbd "C-x C-z"))
-
-;; Replace `kill-word', `backward-kill-word', and `kill-visual-line' with
-;; equivalents that do not copy the region into the kill ring.
-
-(defun delete-word (arg)
- "Delete characters forward until encountering the end of a word.
-With argument ARG, do this that many times."
- (interactive "p")
- (delete-region (point) (progn (forward-word arg) (point))))
-
-(defun backward-delete-word (arg)
- "Delete characters backward until encountering the beginning of a word.
-With argument ARG, do this that many times."
- (interactive "p")
- (delete-word (- arg)))
-
-(defun delete-visual-line (&optional arg)
- "Delete the rest of the visual line.
-With prefix argument ARG, delete that many visual lines from point.
-If ARG is negative, delete visual lines backward.
-If ARG is zero, delete the text before point on the current visual
-line."
- (interactive "P")
- ;; Like in `kill-line', it's better to move point to the other end
- ;; of the kill before killing.
- (let ((opoint (point))
- (delete-whole-line (and kill-whole-line (bolp))))
- (if arg
- (vertical-motion (prefix-numeric-value arg))
- (end-of-visual-line 1)
- (if (= (point) opoint)
- (vertical-motion 1)
- ;; Skip any trailing whitespace at the end of the visual line.
- ;; We used to do this only if `show-trailing-whitespace' is
- ;; nil, but that's wrong; the correct thing would be to check
- ;; whether the trailing whitespace is highlighted. But, it's
- ;; OK to just do this unconditionally.
- (skip-chars-forward " \t")))
- (delete-region opoint (if (and delete-whole-line (= (following-char) ?\n))
- (1+ (point))
- (point)))))
-
-(bind-key "M-d" #'delete-word)
-(bind-key "M-DEL" #'backward-delete-word)
-(bind-key "C-k" #'delete-visual-line)
-
-;; I use <M-v> and <M-V> for movement, mostly, so having <prior> and <next>
-;; bound to the same functions is a little redundant. What I prefer is to bind
-;; them to functions for scolling by one line at a time instead. Sometimes
-;; useful when perusing IRC logs.
-
-(define-minor-mode baby-scroll-mode
- "Toggle `baby-scroll-mode'.
-Minor mode which remaps <next> and <prior> to less extreme
-scrolling functions."
- :global t
- nil "Baby Scroll" '(([next] . scroll-up-line)
- ([prior] . scroll-down-line)))
-
-(baby-scroll-mode)
-
-;; Don't do jerky jumps when the cursor reaches the end of the window. Instead,
-;; just scroll by one line.
-
-(setq scroll-conservatively most-positive-fixnum)
-
-
-;;;
-;;; Appearance
-;;;
-
-;; Use soft wrapping instead of "scrolling" when overflowing the width of a frame.
-
-(global-visual-line-mode t)
-
-;; The menu bar can be accessed with <M-`> if I really need it, so I don't see
-;; the point in having it take up the space at the top. The rest of these rarely
-;; see use from me, and I'd prefer to disable them and have access to the extra
-;; screen real estate.
-
-(menu-bar-mode -1)
-(scroll-bar-mode -1)
-(tool-bar-mode -1)
-(fringe-mode -1)
-
-;; Hacking is a lot more serene when visual stimulus is kept to a minimum.
-
-(blink-cursor-mode 0)
-
-;; Enable `prettify-symbols' globally.
-
-(global-prettify-symbols-mode t)
-
-;; This is snippet was taken verbatim from Emacs StackExchange [1] to enable
-;; `prettify-symbols in Org source code blocks. Sadly, I can't think of a way to
-;; implement this as advice.
-;;
-;; [1]: https://emacs.stackexchange.com/questions/17283/is-it-possible-to-get-prettified-symbols-in-org-mode-source-blocks
-
-(defun org-src-font-lock-fontify-block (lang start end)
- "Fontify code block.
-This function is called by emacs automatic fontification, as long
-as `org-src-fontify-natively' is non-nil."
- (let ((lang-mode (org-src--get-lang-mode lang)))
- (when (fboundp lang-mode)
- (let ((string (buffer-substring-no-properties start end))
- (modified (buffer-modified-p))
- (org-buffer (current-buffer)) pos next)
- (remove-text-properties start end '(face nil))
- (with-current-buffer
- (get-buffer-create
- (concat " org-src-fontification:" (symbol-name lang-mode)))
- (delete-region (point-min) (point-max))
- (insert string " ") ;; so there's a final property change
- (unless (eq major-mode lang-mode) (funcall lang-mode))
- ;; Avoid `font-lock-ensure', which does not display fonts in
- ;; source block.
- (font-lock-fontify-buffer)
- (setq pos (point-min))
- (while (setq next (next-single-property-change pos 'face))
- (put-text-property
- (+ start (1- pos)) (1- (+ start next)) 'face
- (get-text-property pos 'face) org-buffer)
- (setq pos next))
- ;; Addition: also copy 'composition info for prettified symbols
- (setq pos (point-min))
- (while (setq next (next-single-property-change pos 'composition))
- (put-text-property
- (+ start (1- pos)) (1- (+ start next)) 'composition
- (get-text-property pos 'composition) org-buffer)
- (setq pos next))
- ;; End addition
- )
- (add-text-properties
- start end
- '(font-lock-fontified t fontified t font-lock-multiline t))
- (set-buffer-modified-p modified)))))
-
-;; Although I'm usually not a fan of treating non-printable ASCII like this, the
-;; lines act as sort more of a cue for me to use <C-x [> and <C-x ]> than a
-;; plain ol' '^L'.
-
-(use-package page-break-lines
- :demand t
- :config
- (global-page-break-lines-mode))
-
-
-;;;
-;;; Theme
-;;;
-
-;; This is my custom theme, the code for which is very loosely adapted from
-;; moe-theme.el [1], but most of the palette has been altered to emulate the
-;; appearance of my old setup of Emacs in a terminal emulator.
-;;
-;; [1]: https://github.com/kuanyui/moe-theme.el
-
-(deftheme chayote
- "Inspired by the appearance of Emacs in my terminal emulator.")
-
-;; The colors have been extracted into a variable, `palette', and a macro,
-;; `with-palette', to provide access to the theme's color definitions without
-;; significantly polluting the global namespace. To help me in modifying the
-;; theme, I've defined a function similar to `list-colors-display', but for
-;; showing the colors in the palette I've defined.
-
-(defvar palette
- '((yellow-1 "#ffd787") (yellow-2 "#ffd700") (yellow-3 "#c4a000")
- (yellow-3-5 "#aaaa11") (yellow-4 "#875f00")
-
- (orange-1 "#ffaf87") (orange-2 "#ff8700") (orange-3 "#ff5d17")
- (orange-4 "#d75f00") (orange-5 "#af5f00")
-
- (magenta-1 "#ff7bbb") (magenta-2 "#ff4ea3") (magenta-3 "#ff1f8b")
-
- (green-1 "#87ff87") (green-2 "#a1db00") (green-3 "#5faf00")
- (green-4 "#008700") (green-5 "#005f00")
-
- (cyan-1 "#afd7d7") (cyan-2 "#87ffd7") (cyan-3 "#00d7af")
- (cyan-4 "#00ac8a") (cyan-5 "#5faf87") (cyan-6 "#005f5f")
- (cyan-7 "#236f73")
-
- (blue-1 "#1fb6d3") (blue-2 "#1f5bff") (blue-3 "#005f87")
- (blue-4 "#5f7ab1") (blue-5 "#0000af") (blue-6 "#00005f")
-
- (purple-1 "#d18aff") (purple-2 "#af5fff") (purple-3 "#9a08ff")
- (purple-4 "#6c0099")
-
- (red-1 "#ef2929") (red-2 "#dd0000") (red-3 "#a40000")
- (red-4 "#5f0000")
-
- (white-1 "#f0e9dc") (white-2 "#c6c6c6") (white-3 "#b2b2b2")
-
- (black-1 "#a8a8a8") (black-2 "#8a8a8a") (black-2-5 "#6c6c6c")
- (black-3 "#4e4e4e") (black-4 "#3a3a3a") (black-5 "#303539")
- (black-6 "#000000")
-
- (LIGHT_BG "#fdfde7") (white-0 "#eeeeee")
-
- (green-02 "#5fd700") (green-01 "#d7ff00") (green-0 "#d7ff5f")
- (green-00 "#d7ff87")
-
- (cyan-0 "#d7ffd7")
-
- (blue-01 "#c3c9f8") (blue-0 "#afd7ff") (blue-00 "#d7d7ff")
-
- (yellow-0 "#ffff87") (yellow-00 "#ffffaf")
-
- (purple-0 "#af87ff") (purple-00 "#e6a8df")
-
- (red-0 "#ff4b4b") (red-00 "#ffafaf")
-
- (magenta-0 "#ffafd7") (magenta-00 "#ffd7ff")
-
- (orange-0 "#ffaf87") (orange-00 "#ffd787") (orange-000 "#ffd7af")
-
- (linum-dark "#87875f") (linum-light "#d7d7af"))
- "The palette of colors in use with my 'chayote' theme.")
-
-(defmacro with-palette (&rest form)
- "Evaluate `FORM' in a `let' containing the theme's colors."
- `(let ((class '((class color) (min-colors 89)))
- ,@palette)
- ,@form))
-
-(defun display-palette ()
- "Display names of colors in `PALETTE', and show what they look like."
- (interactive)
- (with-help-window "*Chayote Colors*"
- (with-current-buffer standard-output
- (erase-buffer)
- (dolist (color palette)
- (let* ((name (symbol-name (first color)))
- (value (second color)))
- (put-text-property 0 (length name)
- 'font-lock-face `(:foreground ,value)
- name)
- (insert name)
- (insert (propertize " " 'display '(space :align-to (- right 9))))
- (insert " ")
- (put-text-property 0 (length value)
- 'font-lock-face `(:background ,value)
- value)
- (insert value)
- (terpri)))
- (set-buffer-modified-p nil)
- (setq truncate-lines t))))
-
-(with-palette
- (custom-theme-set-faces
- 'chayote
- `(default ((,class (:foreground ,white-1 :background ,black-5 :family "Terminus" :height 90))))
- `(italic ((,class (:italic t))))
- `(cursor ((,class (:background ,white-0))))
- `(variable-pitch ((,class (:inherit default))))
-
- `(link ((,class (:foreground ,blue-1 :underline t))))
-
- ;; Highlighting faces
- `(highlight ((,class (:background ,black-3))))
- `(hl-line ((,class (:background ,black-4))))
- `(highlight-symbol-face ((,class (:background ,green-5))))
- `(region ((,class (:background ,blue-4))))
- `(secondary-selection ((,class (:background ,blue-3 :foreground ,white-0))))
- `(isearch ((,class (:foreground ,white-1 :background ,orange-3))))
- `(lazy-highlight ((,class (:background ,magenta-3 :foreground ,white-1))))
- `(trailing-whitespace ((,class (:background ,red-3))))
- `(show-paren-match ((,class (:background ,blue-3 :foreground nil))))
- `(header-line ((,class (:background ,blue-3 :foreground ,white-0))))
- `(help-argument-name ((,class (:foreground ,magenta-1 :italic t))))
- `(eldoc-highlight-function-argument ((,class (:foreground ,green-01 :bold t :underline t :background ,green-5))))
-
- ;; Mode line & frames' faces
- `(mode-line ((,class (:box nil :background ,white-1 :foreground ,black-5))))
- `(mode-line-inactive ((,class (:background ,white-3 :foreground ,black-5))))
- `(mode-line-buffer-id ((,class (:box nil :foreground ,black-5 :background nil))))
- `(vertical-border ((,class (:foreground ,black-3 :background ,black-3))))
-
- ;; Escape and prompt faces
- `(minibuffer-prompt ((,class (:foreground ,blue-1))))
- `(escape-glyph ((,class (:foreground ,yellow-3))))
- `(error ((,class (:foreground ,red-0))))
- `(warning ((,class (:foreground ,orange-1))))
- `(success ((,class (:foreground ,green-1))))
-
- ;; Font lock faces
- `(font-lock-builtin-face ((,class (:foreground ,cyan-1))))
- `(font-lock-comment-delimiter-face ((,class (:inherit font-lock-comment-face))))
- `(font-lock-comment-face ((,class (:foreground ,orange-2))))
- `(font-lock-constant-face ((,class (:foreground ,cyan-2))))
- `(font-lock-doc-face ((,class (:inherit font-lock-string-face))))
- `(font-lock-doc-string-face ((,class (:inherit font-lock-string-face))))
- `(font-lock-function-name-face ((,class (:foreground ,blue-0))))
- `(font-lock-keyword-face ((,class (:foreground ,blue-1))))
- `(font-lock-negation-char-face ((,class (:foreground ,red-0))))
- `(font-lock-preprocessor-face ((,class (:inherit font-lock-builtin-face))))
- `(font-lock-regexp-grouping-backslash ((,class (:foreground ,yellow-1))))
- `(font-lock-regexp-grouping-construct ((,class (:foreground ,purple-1))))
- `(font-lock-string-face ((,class (:foreground ,orange-1))))
- `(font-lock-type-face ((,class (:foreground ,green-1))))
- `(font-lock-variable-name-face ((,class (:foreground ,yellow-1))))
- `(font-lock-warning-face ((,class (:weight bold :foreground ,red-2))))
-
- ;; Info
- `(Info-quoted ((,class (:inherit default))))
-
- ;; company
- `(company-tooltip ((,class (:background ,black-4))))
- `(company-scrollbar-bg ((,class (:background ,black-4))))
- `(company-scrollbar-fg ((,class (:background ,orange-2))))
- `(company-tooltip-selection ((,class (:background ,black-3))))
- `(company-tooltip-common ((,class (:foreground ,blue-1))))
- `(company-tooltip-annotation ((,class (:foreground ,cyan-1))))
-
- ;; AUCTeX
- `(font-latex-sedate-face ((,class (:inherit font-lock-keyword-face))))
-
- ;; Gnus
- `(gnus-group-mail-3 ((,class (:foreground ,green-2))))
-
- ;; org-mode
- `(org-document-info-keyword ((,class (:foreground ,red-0))))
- `(org-document-title ((,class (:inherit org-document-info-keyword))))
- `(org-document-info ((,class (:inherit org-document-info-keyword))))
- `(org-meta-line ((,class (:inherit org-document-info-keyword))))
- `(org-archived ((,class (:slant italic))))
- `(org-checkbox ((,class (:background ,white-2 :foreground ,black-3
- :box (:line-width 1 :style released-button)))))
- `(org-date ((,class (:foreground ,green-3 :underline t))))
- `(org-done ((,class (:foreground ,green-4 :background ,green-00
- :box (:line-width 1 :style none)))))
- `(org-todo ((,class (:foreground ,red-3 :background ,orange-0
- :box (:line-width 1 :style none)))))
- `(org-level-1 ((,class (:foreground ,purple-00))))
- `(org-level-2 ((,class (:foreground ,blue-0))))
- `(org-level-3 ((,class (:foreground ,cyan-3))))
- `(org-level-4 ((,class (:foreground ,green-02))))
- `(org-level-5 ((,class (:foreground ,red-2))))
- `(org-level-6 ((,class (:foreground ,orange-2))))
- `(org-level-7 ((,class (:foreground ,magenta-2))))
- `(org-level-8 ((,class (:foreground ,yellow-2))))
- `(org-tag ((,class (:background ,black-1 :foreground ,white-0))))
- `(org-column ((,class (:background ,yellow-00 :foreground ,black-3))))
- `(org-column-title ((,class (:background ,blue-0 :foreground ,black-5 :underline t))))
- `(org-agenda-structure ((,class (:foreground ,cyan-4 :background nil :bold nil))))
- `(org-deadline-announce ((,class (:foreground ,red-3))))
- `(org-scheduled ((,class (:foreground ,black-2))))
- `(org-scheduled-previously ((,class (:foreground ,red-1))))
- `(org-scheduled-today ((,class (:foreground ,blue-2))))
- `(org-special-keyword ((,class (:foreground ,green-3))))
- `(org-table ((,class (:background ,black-4 :foreground ,white-3))))
- ;; `(org-time-grid ((,class (:foreground ,white-4))))
- `(org-upcoming-deadline ((,class (:foreground ,red-3))))
- ;; `(org-warning ((,class (:foreground ,white-0 :background ,red-3))))
- `(org-warning ((,class (:foreground ,white-0 :background ,red-4))))
- `(org-formula ((,class (:foreground ,purple-2))))
- `(org-headline-done ((,class (:foreground ,green-2))))
- `(org-hide ((,class (:foreground ,LIGHT_BG))))
- `(org-code ((,class (:foreground ,blue-2 :background ,white-1))))
- ;; `(org-link ((,class (:foreground ,blue-2 :underline ,blue-2))))
- `(org-link ((,class (:inherit link))))
- `(org-footnote ((,class (:foreground ,magenta-3))))
- `(org-ellipsis ((,class (:foreground ,red-2))))
- `(org-agenda-clocking ((,class (:foreground ,blue-3 :background ,blue-0))))
- `(org-agenda-date ((,class (:foreground ,blue-1 :background nil :underline nil))))
- `(org-agenda-date-weekend ((,class (:foreground ,purple-2 :background nil :slant italic :bold nil))))
- `(org-agenda-date-today ((,class (:foreground ,blue-0 :background nil :underline t))))
- `(org-agenda-column-dateline ((,class (:foreground ,white-0 :background ,yellow-0 :underline t))))
- `(org-agenda-todo ((,class (:foreground ,white-0 :background ,red-2))))
- `(org-agenda-done ((,class (:foreground ,green-3 :background ,nil))))
- `(org-agenda-dimmed-todo-face ((,class (:foreground ,white-0 :background ,red-2))))
- `(org-priority ((,class (:foreground ,red-1 :background ,nil))))
- `(org-block ((,class (:foreground ,white-1 :background ,black-4 :extend t))))
- `(org-block-begin-line ((,class (:foreground ,black-3))))
- `(org-block-end-line ((,class (:foreground ,black-3))))
- `(org-block-background ((,class (:background ,white-1))))
- ;; `(org-block-begin-line ((,class (:foreground ,blue-3 :background ,blue-0))))
- ;; `(org-block-end-line ((,class (:foreground ,white-2 :background ,white-1))))
- `(org-quote ((,class (:foreground ,blue-3 :background ,blue-00))))
- `(org-mode-line-clock ((,class (:foreground ,blue-3 :background ,blue-0 :bold t))))
- `(org-mode-line-clock-overrun ((,class (:foreground ,white-0 :background ,red-1 :bold t))))
- ;; `(org-verbatim ((,class (:foreground ,blue-3 :background ,blue-00 :bold nil))))
- `(org-verbatim ((,class (:foreground ,blue-1 :background ,black-4))))
-
- ;; erc
- `(erc-input-face ((,class (:foreground ,white-1))))
- `(erc-button ((,class (:inherit link))))
-
- ;; smartparens
- `(sp-pair-overlay-face ((,class (:inherit default)))))
-
- (custom-theme-set-variables
- 'chayote
- `(ansi-color-names-vector [,black-5 ,red-0 ,green-0 ,yellow-1
- ,blue-1 ,purple-1 ,blue-0 ,white-1])))
-
-(enable-theme 'chayote)
-
-
-;;;
-;;; Modeline
-;;;
-
-;; My implementation of a clock for the modeline. The modeline is refreshed by a
-;; timer to ensure that it doesn't go too stale, as it could theoretically go
-;; several minutes without being updated.
-
-(defun mode-line-time ()
- (format-time-string "%Y-%m-%d %H:%M"))
-
-(let ((timer (run-at-time t 60.0 #'ignore)))
- (timer-set-function timer
- (lambda ()
- (force-mode-line-update))))
-
-;; For some time, I was using my own implementation of a modeline battery
-;; monitor. However, the constant calls to `battery-linux-sysfs' were
-;; interfering with the way that EXWM handles input focus, and I've since moved
-;; towards reusing `display-battery-mode'. Because strings in `mode-line-format'
-;; can contain control sequences, the percent symbol has to be escaped twice --
-;; once for `battery-format', and once for `mode-line-format'.
-;;
-;; Obviously, my tower lacks a battery, so I only enable this on my laptop.
-
-(when (string= (system-name) "Upsilon")
- (require 'battery)
- (display-battery-mode +1)
- (setq battery-mode-line-format "%L: %t, %p%%%%"))
-
-(defun maybe-elide (s &optional max-length)
- "Truncate `S' to `MAX-LENGTH'.
-Return a string derived from `S', possible elided, that is at
-most 1MAX-LENGTH characters long."
- (let ((max-length (or max-length 30)))
- (if (> (length s) max-length)
- (concat (substring s 0 (- max-length 4)) " ...")
- s)))
-
-(defun escape-mode-line-construct (s)
- "Sanitize `S' for use in `mode-line-format'.
-Ensure that no part of S will be interpreted as a modeline format
-specifier."
- (replace-regexp-in-string (regexp-quote "%") "%%" s t t))
-
-(defun mode-line-render (left right)
- "Return a string of `window-width' length containing LEFT, and
-RIGHT aligned respectively."
- (let* ((right (format-mode-line right))
- (left (maybe-elide (format-mode-line left)
- (- (window-width) (length right) 1)))
- (padding (- (window-width) (length left) (length right) 1))
- (padding (if (wholenump padding) (make-string padding ?\ ))))
- (if padding
- (escape-mode-line-construct (concat " " left padding right))
- (concat " " left right))))
-
-(defun remove-unicode (s)
- "Remove non-ASCII or non-printable ASCII characters from `S'."
- (apply #'string (seq-filter #'(lambda (c) (and (>= c ? ) (<= c ?~))) s)))
-
-(setq-default mode-line-format
- '((:eval
- (mode-line-render
- (cond ((eq major-mode 'exwm-mode)
- (concat "%b: " (remove-unicode exwm-title)))
- ((eq major-mode 'pdf-view-mode)
- (concat "%b ["
- (number-to-string (pdf-view-current-page))
- "/"
- (number-to-string (pdf-cache-number-of-pages))
- "]"))
- (t
- (concat "%b [%m] [%*] Ln: %l/"
- (number-to-string (1+ (count-lines 1 (point-max))))
- " Col: %c")))
- (cond ((string= (system-name) "Upsilon")
- (format "%s | %s"
- (mode-line-time)
- battery-mode-line-string))
- (t (mode-line-time)))))))
-
-
-;;;
-;;; Ivy
-;;;
-
-;; As suggested by a friend [1], I tried out `ivy' as a replacement for `ido.'
-;; It was love at first sight. It's snappy, and works even better than `ido'
-;; after setting `exwm-workspace-show-all-buffers' and
-;; `exwm-layout-show-all-buffers'.
-;;
-;; [1]: https://icosahedron.website/@zge/101330910258980695
-
-(use-package ivy
- ;; Configure how the =completing-read= is displayed.
- :init
- (setq ivy-height 5 ivy-count-format "")
-
- ;; Enable Ivy globally.
- :config
- (ivy-mode 1))
-
-;; `counsel' is a nice Ivy-based frontend for a couple of interactive commands.
-
-(use-package counsel
- :demand t
- :bind
- ("C-x b" . counsel-switch-buffer))
-
-;; Use Swiper instead of isearch.
-
-;; TODO: Should I re-enable this?
-
-;; (use-package swiper
-;; :bind
-;; ("C-r" . swiper)
-;; ("C-s" . swiper))
-
-
-;;;
-;;; Imenu
-;;;
-
-(use-package imenu
- :bind ("C-c s" . imenu))
-
-
-;;;
-;;; Yasnippet
-;;;
-
-;; Load all snippets when `yas-minor-mode' is enabled.
-
-(use-package yasnippet
- :demand t
- :hook
- (yas-minor-mode . yas-reload-all))
-
-
-;;;
-;;; Avy
-;;;
-
-;; Set up keybindings for Avy.
-
-(use-package avy
- :bind
- ("M-s" . avy-goto-char))
-
-;; Install a link-selection package for Avy, and create a binding for `org-mode'.
-
-;; TODO: Should I re-enable this?
-
-;; (use-package ace-link
-;; :after org
-;; :init (ace-link-setup-default)
-;; :bind (:map org-mode-map
-;; ("C-c C-o" . ace-link-org)))
-
-
-
-;;;
-;;; pass
-;;;
-
-;; TODO: Replace with pass.el?
-
-;; Here's a snippet for interactively pulling a password from password-store, so
-;; I'm not spawning a terminal every time I need to invoke my password manager.
-
-(defvar password-store-directory "~/.password-store"
- "Directory containing passwords managed by `pass'.")
-
-(defun pass-password-files ()
- "Returns the paths of every password managed by `pass'."
- (let ((prefix-length (1+ (length password-store-directory)))
- (suffix-length (length ".gpg")))
- (map 'list
- #'(lambda (path) (cl-subseq path prefix-length (- suffix-length)))
- (directory-files-recursively password-store-directory ".gpg$"))))
-
-(defun pass ()
- "Prompt for a password and copy it to the clipboard.
-Passwords are taken from `password-store-directory'."
- (interactive)
- (let* ((account (completing-read "Account: " (pass-password-files)))
- (path (concat password-store-directory "/" account ".gpg")))
- (with-temp-buffer
- (insert-file-contents path)
- (clipboard-kill-ring-save 1 (1- (buffer-end 1))))
- (message "Password for %s copied to clipboard." account)))
-
-
-;;;
-;;; 8-ball
-;;;
-
-;; A little snippet for getting an answer to a dumb question. Responses were
-;; lifted from Wikipedia's page [1] about the toy.
-;;
-;; [1]: https://en.wikipedia.org/wiki/Magic_8-Ball
-
-(defun consult ()
- "Consult the magic 8-ball.
-Interactive function to consult the magic 8-ball for answers to
-very important questions."
- (interactive)
- (let ((responses (list "It is certain." "It is decidedly so."
- "Without a doubt." "Yes - definitely."
- "You may rely on it." "As I see it, yes."
- "Most likely." "Outlook good." "Yes."
- "Signs point to yes." "Reply hazy, try again."
- "Ask again later." "Better not tell you now."
- "Cannot predict now." "Concentrate and ask again."
- "Don't count on it." "My reply is no."
- "My sources say no." "Outlook not so good."
- "Very doubtful.")))
- (message (elt responses (mod (random) (length responses))))))
-
-
-;;;
-;;; Window Management
-;;;
-
-;; Yes, I use Emacs as my window manager. You should, too!
-
-(use-package exwm
- :config
- (exwm-enable)
-
- ;; I find that four workspaces is enough to start out with, and I like being
- ;; able to use my X windows across workspaces.
- :init
- (setq exwm-workspace-number 4
- exwm-workspace-show-all-buffers t
- exwm-layout-show-all-buffers t)
-
- ;; "Global key bindings" in EXWM work essentially anywhere, including buffers
- ;; that are currently in char mode. I've taken this as an opportunity to
- ;; incorporate windmove to my workflow, now that I have the super key at my
- ;; disposal.
- :init
- (require 'windmove)
-
- ;; Define some interactive functions for dealing with a multi-head setup.
- (when (string= (system-name) "Epsilon")
- (defun my-windmove-right ()
- (interactive)
- (if (windmove-find-other-window 'right)
- (windmove-right)
- (if (= exwm-workspace-current-index 1)
- (exwm-workspace-switch 0)
- (exwm-workspace-switch 1))))
- (defun my-windmove-left ()
- (interactive)
- (if (windmove-find-other-window 'left)
- (windmove-left)
- (if (= exwm-workspace-current-index 1)
- (exwm-workspace-switch 0)
- (exwm-workspace-switch 1)))))
-
- ;; And if we haven't defined those functions because we aren't on a multi-head
- ;; setup, define aliases to the underlying windmove functions.
- (when (and (not (fboundp #'my-windmove-left))
- (not (fboundp #'my-windmove-right)))
- (defalias #'my-windmove-left #'windmove-left)
- (defalias #'my-windmove-right #'windmove-right))
-
- (setq exwm-input-global-keys
- `(([?\s-r] . exwm-reset)
- ([?\s-a] . my-windmove-left)
- ([?\s-d] . my-windmove-right)
- ([?\s-w] . windmove-up)
- ([?\s-s] . windmove-down)
- ([?\s-p] . (lambda (command)
- (interactive (list (read-shell-command "$ ")))
- (start-process-shell-command command nil command)))
- ([?\s-X] . (lambda ()
- (interactive)
- (start-process "" nil "slock")))
- ([?\s-f] . (lambda ()
- (interactive)
- (start-process "" nil "firefox")))
- (,(kbd "s-<return>") . (lambda ()
- (interactive)
- (start-process "" nil "alacritty")))
-
- ;; Keys are offset such that <s-1> corresponds to the 0th workspace.
- ,@(mapcar (lambda (i)
- `(,(kbd (format "s-%d" i)) .
- (lambda ()
- (interactive)
- (exwm-workspace-switch-create ,(- i 1)))))
- (number-sequence 1 9))))
-
- ;; Ah, yes. Simulation keys, the reason I decided to look at EXWM in the first
- ;; place.
- :init
- (setq exwm-input-simulation-keys
- '(([?\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])
- ([?\M-v] . [prior])
- ([?\C-v] . [next])
- ([?\C-d] . [delete])
- ([?\C-k] . [S-end delete])
-
- ([?\C-w] . [?\C-x])
- ([?\M-w] . [?\C-c])
- ([?\C-y] . [?\C-v])
-
- ([?\C-s] . [?\C-f])))
-
- ;; The tab management functionality of Firefox's Saka Key extension is
- ;; limited, so I'm using simulation keys instead. I've also disabled
- ;; simulation keys for alacritty because it doesn't play nicely with readline.
- :hook
- (exwm-manage-finish . (lambda ()
- (when (and exwm-class-name
- (string= exwm-class-name "Firefox"))
- (exwm-input-set-local-simulation-keys
- (concatenate 'list
- exwm-input-simulation-keys
- '(([?\C-q] . [?\C-w])))))
- (when (and exwm-class-name
- (string= exwm-class-name "Alacritty"))
- (exwm-input-set-local-simulation-keys nil))))
-
- ;; EXWM's buffers are named '*EXWM*' upon creation, which makes switching
- ;; between windows a pain. This snippet renames buffers when their class or
- ;; title changes.
- :hook
- (exwm-update-class . (lambda ()
- (exwm-workspace-rename-buffer exwm-class-name)))
- (exwm-update-title . (lambda ()
- (when (not exwm-instance-name)
- (exwm-workspace-rename-buffer exwm-title))))
-
-
- ;; Start the Emacs daemon so that 'emacsclient' can be used from the command
- ;; line to pop a new buffer for editing.
- :config
- (server-start)
-
- ;; Configure the resolution of my monitors and let EXWM know about them.
- :config
- (require 'exwm-randr)
- (defun hdmi-out ()
- (and (string= (system-name) "Upsilon")
- (save-window-excursion
- (with-temp-buffer
- (shell-command "xrandr" (current-buffer))
- (cl-search "HDMI-1 connected" (buffer-string))))))
- (setq exwm-randr-workspace-monitor-plist
- (if (string= (system-name) "Epsilon")
- '(0 "DVI-D-0" 1 "HDMI-A-0")
- '(0 (if (hdmi-out) "HDMI-1" "eDP-1"))))
- (add-hook 'exwm-randr-screen-change-hook
- (lambda ()
- (start-process-shell-command
- "xrandr" nil
- (if (string= (system-name) "Epsilon")
- "xrandr --output DVI-D-0 --primary --left-of HDMI-A-0"
- (if (hdmi-out)
- "xrandr --output HDMI-1 --mode 1920x1080 --output eDP-1 --off"
- "xrandr --output eDP-1 --mode 1920x1080")))))
- (exwm-randr-enable))
-
-;; Some keybindings for buffer-move that are analagous to my keybindings for
-;; windmove above.
-
-(use-package buffer-move
- :bind
- ("M-s-w" . buf-move-up)
- ("M-s-a" . buf-move-left)
- ("M-s-s" . buf-move-down)
- ("M-s-d" . buf-move-right))
-
-;; EXWM has issues popping any sort of pinentry window, so I just let Emacs do
-;; pinentry's job.
-
-(use-package epa
- :straight nil
- :config
- (setq epa-pinentry-mode 'loopback))
-
-;; And on that note, allow 'gpg-agent' to query Emacs for my passphrase.
-
-(use-package pinentry
- :demand t
- :config
- (setenv "INSIDE_EMACS" (format "%s,comint" emacs-version))
- (pinentry-start))
-
-
-;;;
-;;; System
-;;;
-
-;; Set up keybindings for calling 'ag'.
-
-(use-package ag
- :bind
- ("C-c k" . counsel-ag))
-
-(use-package dired
- :straight nil
- ;; Hide the targets of symbolic links in `dired' listings. This is a visual
- ;; preference of mine.
- :init
- (setq-default dired-listing-switches "-alL")
-
- ;; Show '.eln' files the same way that '.elc' files are.
- :config
- (push ".eln" completion-ignored-extensions))
-(use-package dired-rmjunk
- ;; Set up a keybinding for invoking `dired-rmjunk' from `dired-mode'.
- :bind
- (:map dired-mode-map
- ("r" . dired-rmjunk))
+;;; Everything above is what I classify as the "early-load" part of my
+;;; configuration. The rest of my configuration is broken into "modules", which
+;;; I include into the init.el at macro expansion time.
- ;; Implement my customizations from =rmshit.py=.
- :init
- (setq dired-rmjunk-patterns
- '(".recently-used"
- ".local/share/recently-used.xbel"
- ".thumbnails"
- ".gconfd"
- ".gconf"
- ".local/share/gegl-0.2"
- ".gstreamer-0.10"
- ".esd_auth"
- ".config/enchant"
- ".parallel"
- ".dbus"
- "ca2"
- "ca2~"
- ".nv/"
- ".npm/"
- ".java/"
- ".oracle_jre_usage/"
- ".jssc/"
- ".tox/"
- ".pylint.d/"
- ".guile_history"
- ".sqlite_history"
- ".python_history"
- ".hy-history"
- ".lesshst"
- ".audacity-data"
- ".wget-hsts"
- ".scapy_history"
- ".gdb_history"
- ".binwalk"
- ".lyrics"
- ".slime-history.eld"
- ".w3m")))
-
-
-;;;
-;;; Documents
-;;;
-
-;; Ensure that my Aspell "personal dictionary" is included when invoking ispell.
-
-(setq ispell-personal-dictionary "~/.aspell.en.pws")
-
-(use-package org
- :straight org-plus-contrib
-
- ;; File system of hygene.
- :init
- (setq-default org-id-locations-file "~/.cache/emacs/org-id-locations"
- org-preview-latex-image-directory "~/.cache/emacs/ltximg/"
- org-preview-latex-image-directory "~/.cache/emacs/ltximg/")
-
- ;; General appearance and quality-of-life settings.
- :init
- (setq org-return-follows-link t
- org-startup-folded nil
- org-cycle-separator-lines -1
- org-startup-with-inline-images t
- org-hide-emphasis-markers t
- org-tags-column 100
- org-yank-folded-subtrees nil
- org-link-elisp-confirm-function nil)
-
- ;; Keep everything in column zero, instead of indenting according to the depth
- ;; of the preceding headline.
-
- :init
- (setq org-adapt-indentation nil)
-
- ;; Have <tab> indent according to the indentation rules of the corresponding
- ;; mode in an =org-src= block, and start indentation on column 0. Taken from
- ;; [1] and [2].
- ;;
- ;; [1]: https://stackoverflow.com/questions/15773354/indent-code-in-org-babel-src-blocks
- ;; [2]: https://emacs.stackexchange.com/questions/9472/org-mode-source-block-doesnt-respect-parent-buffer-indentation
- :init
- (setq org-src-tab-acts-natively t
- org-edit-src-content-indentation 0)
-
- ;; Reuse the current window when I punch in <C-c '>, rather than mess up my
- ;; window configuration.
- :init
- (setq org-src-window-setup 'current-window)
-
- ;; Enable Geiser evaluation for Scheme blocks.
- :config
- (org-babel-do-load-languages
- 'org-babel-load-languages
- '((scheme . t)))
-
- ;; Scale up embedded LaTeX. The default size is unreadable.
- :config
- (setq org-format-latex-options
- (plist-put org-format-latex-options :scale 1.5))
-
- ;; Don't interpret text following underscores as subscript text unless it is
- ;; enclosed in braces.
- :init
- (setq org-use-sub-superscripts "{}")
-
- ;; Enable Yasnippet and Flyspell.
- :hook
- (org-mode . yas-minor-mode-on)
- (org-mode . flyspell-mode)
-
- ;; Replace my Imenu binding with `org-goto,' as it allows me to select parent
- ;; headings.
- :init
- (setq org-goto-interface 'outline-path-completionp)
- (setq org-outline-path-complete-in-steps nil)
-
- :bind
- (:map org-mode-map ("C-c s" . org-goto))
-
- ;; Global keybindings for my Org workflow.
- :bind
- ("C-c a" . (lambda () (interactive) (org-agenda nil "d")))
- ("C-c c" . (lambda () (interactive) (org-capture nil "c")))
- ("C-c t" . (lambda () (interactive) (org-capture nil "t")))
-
- ;; Use Counsel's functionality for adding tags.
- :bind
- (:map org-mode-map ("C-c C-q" . counsel-org-tag))
-
- ;; I maintain a personal wiki using Org and I like to link between pages. This
- ;; is just a convenience link type, rooted at the base of my notes folder so
- ;; that I can easily refer to other pages from subdirectories of
- ;; `notes-directory'.
- :config
- (defvar notes-directory "~/Notes"
- "Where all the pages of my 'personal wiki' are stored.")
-
- (defun org-notes-follow (link)
- "Open LINK, an Org link from a notes buffer.
-Visits LINK, or if LINK names a subdirectory of
-`notes-directory', the 'Index.org' in LINK."
- (let ((directory-p (file-directory-p (expand-file-name link notes-directory))))
- (find-file (expand-file-name (if directory-p "Index.org" (concat link ".org"))
- notes-directory))))
-
- (org-link-set-parameters "note" :follow #'org-notes-follow)
-
- ;; Code for interactively navigating the personal wiki.
- :config
- (defvar notes-directory-ignore
- '("." ".." ".git" ".gitignore" ".strfolder" "Drill" "GTD")
- "Files and directories in `notes-directory' to exclude from
- wiki interactions.")
-
- (defun notes-file-is-page (filename)
- "Returns whether or not FILENAME names a page rather than a category."
- (string= (cl-subseq filename (- (length filename) 4)) ".org"))
-
- (defun notes-file-should-ignore (filename)
- "Returns whether or not a file in `notes-directory' should be ignored.
- Members of `notes-directory-ignore' are ignored."
- (member filename notes-directory-ignore))
-
- (defun notes-hierarchy-paths (&optional category)
- "Returns page and category paths relative to `notes-directory'.
- The resultant list does not include files that satisfy the
- `notes-file-should-ignore' predicate."
- (let* ((path (if category
- (concat notes-directory "/" category)
- notes-directory))
- (contents (cl-remove-if #'notes-file-should-ignore
- (directory-files path)))
- (pages (cl-remove-if-not #'notes-file-is-page contents))
- (categories (cl-remove-if-not #'(lambda (subcategory)
- (file-directory-p (concat path "/" subcategory)))
- contents))
- (categories (if category
- (map 'list
- #'(lambda (subcategory) (concat category "/" subcategory))
- categories)
- categories)))
- (append (if category
- (map 'list #'(lambda (page) (concat category "/" page)) pages)
- pages)
- categories
- (apply #'append (map 'list #'notes-hierarchy-paths categories)))))
-
- (defun notes-hierarchy ()
- "Returns every page and category in `notes-directory'.
- The result contains lists of the form (NAME PATH IS-PAGE-P), and
- does not include files that satisfy the
- `notes-file-should-ignore' predicate."
- (map 'list
- #'(lambda (path) (list
- (cond ((cl-search "Index.org" path)
- (let ((name (cl-subseq path 0 -9)))
- (if (string= name "")
- "/"
- name)))
- ((notes-file-is-page path) (cl-subseq path 0 -4))
- (t path))
- (concat notes-directory "/" path)
- (notes-file-is-page path)))
- (notes-hierarchy-paths)))
-
- (defun notes-page-goto ()
- "Interactively find a page in `notes-directory'."
- (interactive)
- (let* ((hierarchy (notes-hierarchy))
- (hierarchy (cl-remove-if-not #'third hierarchy))
- (name (ivy-read "Find file: " (map 'list #'first hierarchy)))
- (item (cl-find-if #'(lambda (item) (string= name (first item))) hierarchy)))
- (if item
- (find-file (second item))
- (let ((path (concat notes-directory "/" name ".org")))
- (find-file path)))))
-
- ;; Set up a key binding for quickly querying the wiki.
- :bind
- ("C-c w" . notes-page-goto)
-
- ;; Low-friction means of adding things to my GTD inbox.
- :config
- (setq org-capture-templates
- `(("t" "TODO" entry (file "~/Notes/GTD/Inbox.org")
- "* TODO %?\n")))
-
-
- ;; A rather convoluted agenda setup to show me:
- ;; - The tasks I've marked as needing to be done today.
- ;; - The inbox items I need to process.
- ;; - My daily habits (implemented as recurring Org TODO's).
- ;; - A view of deadlines and scheduled items for the week ahead.
- :init
- (setq org-agenda-files '("~/Notes/GTD/Habits.org"
- "~/Notes/GTD/Inbox.org"
- "~/Notes/GTD/Actionable.org"))
-
- (let ((entry-done
- '(lambda () (org-agenda-skip-entry-if 'todo 'done)))
-
- (subtree-is-habit
- '(lambda ()
- (let ((subtree-end (save-excursion (org-end-of-subtree t))))
- (if (string= (org-entry-get nil "STYLE") "habit")
- subtree-end
- nil))))
-
- (subtree-is-not-habit
- '(lambda ()
- (let ((subtree-end (save-excursion (org-end-of-subtree t))))
- (if (not (string= (org-entry-get nil "STYLE") "habit"))
- subtree-end
- nil))))
-
- (subtree-is-not-inbox
- '(lambda ()
- (let ((subtree-end (save-excursion (org-end-of-subtree t))))
- (if (not (cl-search "Inbox.org" (buffer-name)))
- subtree-end
- nil)))))
-
- (setq org-agenda-custom-commands
- `(("d" "Daily"
- ((tags "PRIORITY=\"A\""
- ((org-agenda-skip-function ,entry-done)
- (org-agenda-overriding-header "Tasks for Today:")))
- (alltodo ""
- ((org-agenda-skip-function ,subtree-is-not-inbox)
- (org-agenda-overriding-header "Inbox:")))
- (agenda ""
- ((org-agenda-span 'day)
- (org-agenda-skip-function ,subtree-is-not-habit)
- (org-deadline-warning-days 0)
- (org-agenda-overriding-header "Habits:")))
- (agenda ""
- ((org-agenda-span 'week)
- (org-agenda-skip-function ,subtree-is-habit)
- (org-agenda-overriding-header "Week Ahead:"))))
- ((org-agenda-compact-blocks t)))))))
-
-(use-package org-drill
- ;; org-drill in upstream Org is unmaintained, and as described in [1], no
- ;; longer works with the latest version of Org. I'm was previously using this
- ;; fork [2], but the maintainer has since begun working on another fork [3]
- ;; which is where I am now.
- ;;
- ;; [1]: https://bitbucket.org/eeeickythump/org-drill/issues/62/org-drill-doesnt-work-with-org-mode-92
- ;; [2]: https://bitbucket.org/eeeickythump/org-drill/issues/63/maintainership
- ;; [3]: https://gitlab.com/phillord/org-drill/-/blob/master/org-drill.el
- :straight (org-drill :type git :host gitlab :repo "phillord/org-drill")
-
- ;; Update LaTeX fragments when showing them to me.
- :init
- (with-eval-after-load 'org-drill
- (defun org-drill--show-latex-fragments ()
- (org-latex-preview '(16))))
-
- ;; Scan the directory of the current buffer's file for other items, rather
- ;; than just the current buffer. This is so that I can spread my cards across
- ;; multiple files and still be tested on all of them when I run `org-drill'.
- ;; The idea of combining topic during review sessions is suggested in Michael
- ;; Nielsen's article [1].
- ;;
- ;; [1]: http://augmentingcognition.com/ltm.html
- :init
- (setq org-drill-scope 'directory)
-
- ;; Use the SM-2 algorithm rather than the default SM-5 algorithm. I have faith
- ;; in Anki's analysis of SM-5 [1] in the form that it is available.
- ;;
- ;; [1]: https://apps.ankiweb.net/docs/manual.html#what-about-sm-5
- :init
- (setq org-drill-spaced-repetition-algorithm 'sm2)
-
- ;; Shut up the nagging "are you studying enough!?" message if I don't get
- ;; enough items right. It's unhelpful and discourages me from being honest
- ;; with the SRS algorithm.
- :init
- (setq org-drill-forgetting-index 100)
-
- ;; Set up a keybinding for getting quickly starting a drill session.
- :bind
- ("<f11>" . (lambda ()
- (interactive)
- (let* ((drill-directory (concat notes-directory "/" "Drill"))
- (path (-first #'(lambda (path) (s-ends-with-p ".org" path))
- (directory-files drill-directory t))))
- (find-file path)
- (org-drill)))))
-
-;; The org-download package is incredible for quickly pull images into Org
-;; documents.
-
-(use-package org-download
- ;; Configure it to use 'scrot' and the 'org-attach' machinery.
- :init
- (setq org-download-screenshot-method "scrot -s %s")
- (setq org-download-method 'attach))
-
-;; When exporting Org as HTML, include syntax highlighting.
-
-(use-package htmlize
- :config
- (setq org-html-htmlize-output-type 'css))
-
-
-;;;
-;;; PDF
-;;;
-
-;; `doc-view' is unbearable for reading PDF's of any considerable size and
-;; `pdf-tools' brings a number of significant improvements, so I can justify not
-;; using the built-in solution. Even though I can run MuPDF in an EXWM buffer,
-;; the integration in terms of searching and viewing attachments that comes with
-;; pdf-tools makes for a much more pleasant experience.
-
-(use-package pdf-tools
- :demand t
- :config
- (pdf-loader-install))
-
-
-;;;
-;;; IRC
-;;;
-
-;; Functions for connecting to and authenticating with my IRC bouncer.
-
-(defun read-bouncer-password ()
- "Pull the password for my bouncer from authinfo"
- (let ((auth (auth-source-search :host "jakob.space" :user "jakob" :port 6697)))
- (unless auth
- (error "No auth entry for bouncer."))
- (let ((secretf (plist-get (car auth) :secret)))
- (unless secretf
- (error "No secret for bouncer's auth entry."))
- (funcall secretf))))
-
-(defvar *bouncer-port-alist* '(("freenode" . 6697)
- ("tildetown" . 6671)))
-
-(defun connect-bouncer (server)
- "Connect to `SERVER' on the bouncer and rename the buffer"
- (let* ((port (cdr (assoc server *bouncer-port-alist*)))
- (buf (erc-tls :server "jakob.space"
- :port port
- :password (read-bouncer-password))))
- (erc-with-buffer (buf (rename-buffer server)))))
-
-;; Interactive function for connecting to all of my bouncer's IRC servers.
-
-(defvar irc-autoconnect-networks '("freenode" "tildetown")
- "List of networks to automatically connect to when 'irc' is invoked.")
-
-(defun irc ()
- "Connect to every network in `irc-autoconnect-networks'."
- (interactive)
- (dolist (network irc-autoconnect-networks)
- (connect-bouncer network)))
-
-(defun irc-quit ()
- "Disconnects from and kills all buffers associated with ERC."
- (interactive)
- (let ((server-buffers (cl-remove-if-not #'erc-server-buffer-p (erc-buffer-list))))
- (dolist (buffer server-buffers)
- (erc-with-buffer (buffer)
- (erc-quit-server (funcall erc-quit-reason nil))))))
-
-(use-package erc
- :straight nil
-
- ;; Only load the modules I actually use. This list deviates from the default
- ;; by including truncate, but omitting autojoin.
- :config
- (setq erc-modules '(button completion fill
- irccontrols list match
- menu move-to-prompt netsplit
- networks noncommands readonly
- ring stamp track truncate))
- (erc-update-modules)
-
- ;; Configure personal details.
- :init
- (setq erc-nick '("jakob" "tsarfox")
- erc-user-full-name "Jakob <http://jakob.space>"
- erc-quit-reason (lambda (reason)
- (or reason (concat "Program received signal "
- "SIGSEGV, Segmentation fault."))))
-
- ;; Display the current nick in the prompt.
- :init
- (setq erc-prompt (lambda () (format "%s>" (erc-current-nick))))
-
- ;; Center everything.
- :init
- (setq erc-fill-function 'erc-fill-static
- erc-fill-column 100
- erc-fill-static-center 22)
-
- ;; Enable MIRC and control sequences so I can partake in figlet spam,
- ;; objectively the best part of the IRC experience.
- :init
- (setq erc-interpret-controls-p t
- erc-interpret-mirc-color t)
-
- ;; Enable timestamps, and show them to the left of the messages.
- :init
- (setq erc-hide-timestamps nil
- erc-insert-timestamp-function 'erc-insert-timestamp-left)
-
- ;; Although 'log' is omitted from `erc-modules', I still explicitly disable
- ;; logging. I can just save the buffer if I want to save a conversation.
- :init
- (setq erc-enable-logging nil
- erc-save-buffer-on-part nil)
-
- ;; Similarly, 'autowaway' is omitted from `erc-modules', but I'm disabling it
- ;; anyway.
- :init
- (setq erc-auto-set-away nil
- erc-auto-discard-away nil)
-
- ;; Kill buffers when I disconnect.
- :init
- (setq erc-kill-buffer-on-part t
- erc-kill-queries-on-quit t
- erc-kill-server-buffer-on-quit t)
-
- ;; Don't get in my way by popping new windows when someone queries me, and
- ;; don't assault me with windows when I connect to my bouncer.
- :init
- (setq erc-auto-query 'bury
- erc-join-buffer 'bury)
-
- ;; Show CTCP requests. Not for any reason in particular, I just like seeing
- ;; when people ping me.
- :init
- (setq erc-paranoid t)
-
- ;; Hide JOIN/PART/QUIT messages from idlers.
- :init
- (setq erc-lurker-hide-list '("JOIN" "PART" "QUIT")
- erc-lurker-threshold-time 3600)
-
- ;; Configure `erc-track' to ignore the less interesting message types.
- :init
- (setq erc-track-exclude-server-buffer t
- erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE"
- "324" "329" "332" "333" "353" "477"))
-
- ;; Enable channel tracking.
- :config
- (erc-track-minor-mode 1)
- (erc-track-mode 1)
-
- ;; Move to the prompt as soon as I begin typing. I've configured the buffer
- ;; text preceding it to be read-only, anyway.
- :config
- (erc-move-to-prompt-enable)
-
- ;; Bind a key for resetting =erc-track=, since I tend to get absolutely
- ;; bombarded upon reconnecting.
- :bind
- (:map erc-mode-map
- ("C-c f" . (lambda ()
- (interactive)
- (setq erc-modified-channels-alist nil)
- (erc-modified-channels-update))))
-
- ;; Let `erc-fill' handle filling.
- :hook
- (erc-mode . (lambda () (auto-fill-mode 0))))
-
-;; Configure 'erc-hl-nicks' to show names using pretty colors.
-
-(use-package erc-hl-nicks
- :after erc
- :config
- (erc-hl-nicks-enable))
-
-;; Display a bar delimiting read and unread messages. Adapted from [1].
-;;
-;; [1]: https://www.emacswiki.org/emacs/ErcBar
-
-(defvar erc-bar-overlay nil
- "Overlay used to set bar")
-
-(defvar erc-bar-threshold 1
- "The minimum number of messages required for the bar to be
-displayed.")
-
-(defun erc-bar-move-back (n)
- "Moves back n message lines. Ignores wrapping, and server
-messages."
- (interactive "nHow many lines ? ")
- (re-search-backward "^.*<.*>" nil t n))
-
-(defun erc-bar-update-overlay ()
- "Update the overlay for current buffer, based on the content of
-erc-modified-channels-alist. Should be executed on window
-change."
- (interactive)
- (let* ((info (assq (current-buffer) erc-modified-channels-alist))
- (count (cadr info)))
- (if (and info (> count erc-bar-threshold))
- (save-excursion
- (goto-char (point-max))
- (when (erc-bar-move-back count)
- (let ((inhibit-field-text-motion t))
- (move-overlay erc-bar-overlay
- (line-beginning-position)
- (line-end-position)
- (current-buffer)))))
- (delete-overlay erc-bar-overlay))))
-
-(with-eval-after-load 'erc
- ;; Run the hook before 'erc-modified-channels-update
- (defadvice erc-track-mode (after erc-bar-setup-hook
- (&rest args) activate)
- (remove-hook 'window-configuration-change-hook 'erc-bar-update-overlay)
- (add-hook 'window-configuration-change-hook 'erc-bar-update-overlay))
-
- (add-hook 'erc-send-completed-hook (lambda (str) (erc-bar-update-overlay))))
-
-;; Display the bar as a highlight on the most recently unread message.
-
-(with-palette
- (setq erc-bar-overlay (make-overlay 0 0))
- (overlay-put erc-bar-overlay 'face `(:background ,black-2)))
-
-
-;;;
-;;; News
-;;;
-
-;; Most of my Gnus configuration is in gnus.el, which is not under version
-;; control because it contains a list of my accounts on various email servers.
-
-;; Stop Gnus from making a mess of my home directory.
-
-(setq gnus-init-file "~/.emacs.d/gnus.el"
- gnus-startup-file "~/.local/share/newsrc"
- gnus-read-newsrc-file nil
- gnus-save-newsrc-file nil
- gnus-directory "~/.local/share/gnus/news/"
- message-directory "~/.local/share/gnus/mail")
-
-;; Sort articles such that newer items are at the top of the summary buffer.
-
-(setq gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-date))
-
-;; Enable Flyspell so I don't make a fool of myself when composing messages.
-
-(add-hook 'message-mode-hook #'flyspell-mode)
-
-;; Use w3m to display HTML emails. Adapted from [1].
-;;
-;; [1]: https://norman.walsh.name/2011/03/10/w3m
-
-(use-package w3m
- :demand t
- :config
- (setq gnus-mime-display-multipart-related-as-mixed nil
- mm-text-html-renderer #'w3m
- mm-inline-text-html-with-images t
- mm-inline-text-html-with-w3m-keymap nil
- w3m-default-display-inline-images t))
-
-;; Encrypt emails such that we can read them as well.
-
-(setq mml-secure-openpgp-encrypt-to-self t)
-
-
-;;;
-;;; Tunes
-;;;
-
-(use-package mpdel
- :demand t
- ;; Use a more sane prefix key for interacting with 'mpdel'.
- :init
- (setq mpdel-prefix-key (kbd "C-c z"))
-
- ;; Start `mpdel-mode', and while we're at it, start the daemon, too.
- :config
- (shell-command "ps -e | grep -q mpd || mpd")
- (mpdel-mode))
-
-
-;;;
-;;; Programming
-;;;
-
-(use-package dumb-jump
- ;; Replace `xref' keybindings instead of using the default ones, since these
- ;; will get overwritten if I'm using a proper tag indexer anyway.
- :config
- (define-key dumb-jump-mode-map (kbd "C-M-g") nil)
- (define-key dumb-jump-mode-map (kbd "C-M-p") nil)
- (define-key dumb-jump-mode-map (kbd "C-M-q") nil)
- (define-key dumb-jump-mode-map (kbd "M-.") #'dumb-jump-go)
- (define-key dumb-jump-mode-map (kbd "M-,") #'dumb-jump-back)
- (dumb-jump-mode))
-
-(use-package company
- ;; Start suggesting completions (almost) as soon as possible.
- :config
- (setq company-idle-delay 0.1
- company-minimum-prefix-length 3)
-
- ;; Use standard movement keys for selecting entries from the completion popup
- ;; instead of those used for minibuffer next/previous.
- :bind
- (:map company-active-map
- ("M-n" . nil)
- ("M-p" . nil)
- ("C-n" . company-select-next)
- ("C-p" . company-select-previous))
-
- ;; And have <tab> responsible for completion rather than <ret>, so that
- ;; company isn't quite as in-the-way.
- :bind
- (:map company-active-map
- ("TAB" . company-complete-selection)
- ("<tab>" . company-complete-selection)
- ("RET" . nil)
- ("<return>" . nil)))
-
-(use-package compile
- :straight nil
- :bind
- ("<f12>" . compile)
-
- ;; Taken from EmacsWiki [1]. The compilation command can be changed by invoking
- ;; compile with a prefix argument, so I don't see the advantage of popping a
- ;; prompt every time.
- ;;
- ;; [1]: https://www.emacswiki.org/emacs/CompileCommand
- :init
- (setq compilation-read-command nil)
-
- ;; Hide the compilation buffer upon successful compilation. I typically don't
- ;; care about the output unless there were errors.
- :config
- (add-hook 'compilation-finish-functions
- (lambda (buf strg)
- (let ((win (get-buffer-window buf 'visible)))
- (when win (delete-window win))))))
-
-(use-package flycheck)
-
-(use-package magit
- :bind
- ("C-c g" . magit-status))
-
-;; After several failed attempts at getting into Smartparens, I've finally
-;; developed a taste for it. Rommel was not lying when he said that "[i]t's like
-;; having cybernetic limbs—it makes you jump higher and punch harder."
-
-(use-package smartparens
- ;; Use the default Smartparens configuration as a base.
- :config
- (require 'smartparens-config)
-
- ;; Enable the Smartparens-flavored replacement for `show-paren-mode'.
- :config
- (show-smartparens-global-mode t)
-
- ;; Enable Smartparens in all modes derived from `prog-mode' or `comint-mode',
- ;; the latter hopefully covering all REPL cases.
- :hook
- (prog-mode . turn-on-smartparens-mode)
- (comint-mode . turn-on-smartparens-mode)
-
- ;; Rebind the default keys for manipulating sexps to the alternatives
- ;; suggested in Emacs and Pairs [1].
- ;;
- ;; [1]: https://ebzzry.io/en/emacs-pairs/
- :bind
- (:map smartparens-mode-map
- ("C-M-a" . sp-beginning-of-sexp)
- ("C-M-e" . sp-end-of-sexp)
- ("C-M-d" . sp-down-sexp)
- ("C-M-u" . sp-backward-up-sexp)
- ("C-M-f" . sp-forward-sexp)
- ("C-M-b" . sp-backward-sexp)
- ("C-M-n" . sp-next-sexp)
- ("C-M-p" . sp-previous-sexp)
-
- ("M-[" . sp-backward-unwrap-sexp)
- ("M-]" . sp-unwrap-sexp)
- ("C-<right>" . sp-forward-slurp-sexp)
- ("C-<left>" . sp-backward-slurp-sexp)
- ("M-<right>" . sp-foward-barf-sexp)
- ("M-<right>" . sp-forward-barf-sexp)
-
- ("C-M-t" . sp-transpose-sexp)
- ("C-M-k" . sp-kill-sexp)
- ("M-k" . sp-backward-kill-sexp))
-
- ;; The following mask `split-line' and `completion-at-point', respectively,
- ;; but I have yet to use either of these, so I don't mind using the keys for
- ;; Smartparens. I use "o" as a mnemonic for "out", and "i" is a mnemonic for
- ;; "in".
- :bind
- (:map smartparens-mode-map
- ("C-M-o" . sp-up-sexp)
- ("C-M-i" . sp-backward-down-sexp)))
-
-(use-package lsp-ui
- ;; The only reason I'm using `lsp-ui' is for integration with Flycheck. The
- ;; features like lenses don't interest me.
- :init
- (setq lsp-ui-sideline-enable nil
- lsp-ui-peek-enable nil
- lsp-ui-doc-enable nil))
-
-;; Disable the default machinery in `lsp-mode'. The reason I'm using the package
-;; in the first place is for its ability to integrate with the other
-;; programming-related packages I use.
-
-(use-package company-lsp
- :commands company-lsp
- :init
- (setq company-lsp-cache-candidates 'auto))
-
-(use-package lsp-mode
- :init
- (setq lsp-prefer-flymake nil
- lsp-enable-xref t)
-
- ;; Filesystem hygene.
- :init
- (setq lsp-session-file "~/.cache/emacs/.lsp-session-v1"
- lsp-auto-guess-root t)
-
- ;; Use `xref' keybindings instead of the default ones.
- :bind
- (:map lsp-mode-map
- ("M-." . lsp-find-definition)
- ("M-," . pop-tag-mark)))
-
-
-;;;
-;;; Rust
-;;;
-
-;; Enable flycheck.
-
-(use-package flycheck-rust
- :after rust-mode
- :hook
- (flycheck-mode . flycheck-rust-setup)
- (rust-mode . flycheck-mode))
-
-;; Enable company, ElDoc, and racer.
-
-(use-package racer
- :init
- (setq racer-cmd "~/.cargo/bin/racer")
- (setq racer-rust-src-path "~/Code/rust/src")
-
- :hook
- (racer-mode . eldoc-mode)
- (racer-mode . company-mode))
-
-(use-package rust-mode
- :hook
- (rust-mode . racer-mode)
-
- ;; Run 'rustfmt' automatically.
- :init
- (setq rust-format-on-save t))
-
-;; Enable keybindings for working with Cargo.
-
-(use-package cargo
- :hook (rust-mode . cargo-minor-mode))
-
-
-;;;
-;;; C
-;;;
-
-(use-package cc-mode
- :straight nil
-
- ;; Generally, I adhere to the Linux kernel coding style [1].
- ;;
- ;; [1]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html
- :hook
- (c-mode . (lambda ()
- (setq indent-tabs-mode t)
- (c-set-style "linux")))
-
- ;; Enable company, Flycheck, and ElDoc, integration for all of which is
- ;; provided by `irony-mode'.
- :hook
- (c-mode . company-mode)
- (c-mode . flycheck-mode)
- (c-mode . eldoc-mode))
-
-(use-package irony
- :hook
- (c-mode . irony-mode)
- (irony-mode . irony-cdb-autosetup-compile-options))
-
-(use-package company-irony
- :config
- (add-to-list 'company-backend 'company-irony))
-
-(use-package flycheck-irony
- :hook
- (flycheck-mode . flycheck-irony-setup))
-
-(use-package irony-eldoc
- :hook
- (irony-mode . irony-eldoc))
-
-
-;;;
-;;; C++
-;;;
-
-(use-package cc-mode
- :straight nil
- ;; Even though the Linux kernel coding style [1] was written to address C, I
- ;; feel its viewpoints on indentation apply equally as well to C++.
- ;;
- ;; [1]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html
- :hook
- (c++-mode . (lambda ()
- (setq indent-tabs-mode t)
- (c-set-style "linux")))
-
- ;; Enable company, Flycheck, and ElDoc, integration for all of which is
- ;; provided by `irony-mode'.
- :hook
- (c++-mode . company-mode)
- (c++-mode . flycheck-mode)
- (c++-mode . eldoc-mode))
-
-(use-package irony
- :hook
- (c++-mode . irony-mode))
-
-
-;;;
-;;; Emacs Lisp
-;;;
-
-(use-package elisp-mode
- :straight nil
- ;; Enable company for both `lisp-interaction-mode' and `emacs-lisp-mode'.
- :hook
- (lisp-interaction-mode . company-mode)
- (emacs-lisp-mode . company-mode)
-
- ;; Avoid omitting information when displaying deeply-nested lists.
- :init
- (setq eval-expression-print-level nil))
-
-;; Helpful is a wonderful improvement upon the vanilla `describe-.*' functions,
-;; capable of showing source code, keybindings, symbol properties, etc.
-
-(use-package helpful
- :bind
- ("C-h f" . helpful-callable)
- ("C-h v" . helpful-variable)
- ("C-h k" . helpful-key)
- ("C-h C" . helpful-command)
- ("C-c C-d" . helpful-at-point))
-
-
-;;;
-;;; Common Lisp
-;;;
-
-;; SLIME is my preferred REPL for working with Common Lisp,
-
-(use-package slime
- ;; and SBCL is my preferred implementation.
- :config
- (setq inferior-lisp-program "sbcl")
-
- ;; Enable Smartparens and Company in the SLIME REPL.
- :hook
- (slime-repl-mode . (lambda ()
- (smartparens-mode +1)
- (company-mode +1)))
-
- ;; Enable the SLIME backend for company.
- :config
- (setq slime-contribs '(slime-fancy slime-company)))
-
-(use-package slime-company
- :after company
- :hook
- (lisp-mode . (lambda () (company-mode +1)))
- :config
- (add-to-list 'company-backends 'company-slime))
-
-
-;;;
-;;; Scheme
-;;;
-
-;; Geiser is my preferred REPL for working with Scheme.
-
-(use-package geiser
- ;; Set up Geiser for all of the Scheme implementations I regularly use.
- :init
- (setq geiser-active-implementations '(chicken guile racket))
- (setq geiser-default-implementation 'guile)
- (setenv "CHICKEN_DOC_REPOSITORY" "/home/jakob/.local/share/chicken/doc/")
-
- ;; Filesystem hygene.
- :init
- (setq geiser-repl-history-filename "~/.cache/emacs/geiser_history"))
-
-
-;;;
-;;; Python
-;;;
-
-;; Set up company.
-
-(use-package company-jedi
- :after company
-
- :hook
- (python-mode . company-mode)
- (python-mode . jedi:setup)
-
- :init
- (setq jedi:complete-on-dot t)
- (setq jedi:tooltip-method nil)
-
- :config
- (add-to-list 'company-backends 'company-jedi))
-
-
-;;;
-;;; Javascript
-;;;
+(defvar module-directory (expand-file-name "lisp" user-emacs-directory)
+ "Directory containing configuration 'modules'.")
-(use-package js
- :straight nil
+(defmacro insert-code-from-file (path)
+ "Read the forms in the file at PATH into a progn."
+ (with-temp-buffer
+ (insert-file-contents path)
+ (goto-char (point-min))
+ (let (forms (eof nil))
+ (while (not eof)
+ (condition-case nil
+ (push (read (current-buffer)) forms)
+ (end-of-file (setq eof t))))
+ `(progn ,@(reverse forms)))))
- ;; Set up `js-mode' to interact with a LSP daemon.
- :requires lsp-mode
- :hook (js-mode . lsp)
- :config
- (lsp-register-client
- (make-lsp-client :new-connection (lsp-stdio-connection "~/.bin/js-ts-stdio")
- :major-modes '(js-mode)
- :server-id 'js-ts-stdio))
+(defmacro load-module (name)
+ "Locate the module NAME and insert its contents as a progn."
+ (let* ((file-name (concat name ".el"))
+ (path (expand-file-name file-name module-directory)))
+ `(insert-code-from-file ,path)))
-;; I have yet to find a style guide I agree with, but one thing common to most
-;; of them that I _do_ agree with is that Javascript is far more readable with
-;; an indentation width of 2.
- :init
- (setq-default js-indent-level 2))
+(load-module "core")
+(load-module "jakob")
+(load-module "appearance")
+(load-module "appearance/theme")
+(load-module "appearance/modeline")
+(load-module "quality-of-life")
+(load-module "window-manager")
+(load-module "system")
+(load-module "documents")
+(load-module "irc")
+(load-module "news")
+(load-module "tunes")
+(load-module "programming")
+(load-module "programming/rust")
+(load-module "programming/c")
+(load-module "programming/c++")
+(load-module "programming/elisp")
+(load-module "programming/common-lisp")
+(load-module "programming/scheme")
+(load-module "programming/python")
+(load-module "programming/javascript")
diff --git a/lisp/appearance.el b/lisp/appearance.el
new file mode 100644
index 0000000..2d365f7
--- /dev/null
+++ b/lisp/appearance.el
@@ -0,0 +1,85 @@
+;;; appearance.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for making Emacs look pretty.
+;;;
+
+
+
+;; Use soft wrapping instead of "scrolling" when overflowing the width of a frame.
+
+(global-visual-line-mode t)
+
+;; The menu bar can be accessed with <M-`> if I really need it, so I don't see
+;; the point in having it take up the space at the top. The rest of these rarely
+;; see use from me, and I'd prefer to disable them and have access to the extra
+;; screen real estate.
+
+(menu-bar-mode -1)
+(scroll-bar-mode -1)
+(tool-bar-mode -1)
+(fringe-mode -1)
+
+;; Hacking is a lot more serene when visual stimulus is kept to a minimum.
+
+(blink-cursor-mode 0)
+
+;; Enable `prettify-symbols' globally.
+
+(global-prettify-symbols-mode t)
+
+;; This is snippet was taken verbatim from Emacs StackExchange [1] to enable
+;; `prettify-symbols in Org source code blocks. Sadly, I can't think of a way to
+;; implement this as advice.
+;;
+;; [1]: https://emacs.stackexchange.com/questions/17283/is-it-possible-to-get-prettified-symbols-in-org-mode-source-blocks
+
+(defun org-src-font-lock-fontify-block (lang start end)
+ "Fontify code block.
+This function is called by emacs automatic fontification, as long
+as `org-src-fontify-natively' is non-nil."
+ (let ((lang-mode (org-src--get-lang-mode lang)))
+ (when (fboundp lang-mode)
+ (let ((string (buffer-substring-no-properties start end))
+ (modified (buffer-modified-p))
+ (org-buffer (current-buffer)) pos next)
+ (remove-text-properties start end '(face nil))
+ (with-current-buffer
+ (get-buffer-create
+ (concat " org-src-fontification:" (symbol-name lang-mode)))
+ (delete-region (point-min) (point-max))
+ (insert string " ") ;; so there's a final property change
+ (unless (eq major-mode lang-mode) (funcall lang-mode))
+ ;; Avoid `font-lock-ensure', which does not display fonts in
+ ;; source block.
+ (font-lock-fontify-buffer)
+ (setq pos (point-min))
+ (while (setq next (next-single-property-change pos 'face))
+ (put-text-property
+ (+ start (1- pos)) (1- (+ start next)) 'face
+ (get-text-property pos 'face) org-buffer)
+ (setq pos next))
+ ;; Addition: also copy 'composition info for prettified symbols
+ (setq pos (point-min))
+ (while (setq next (next-single-property-change pos 'composition))
+ (put-text-property
+ (+ start (1- pos)) (1- (+ start next)) 'composition
+ (get-text-property pos 'composition) org-buffer)
+ (setq pos next))
+ ;; End addition
+ )
+ (add-text-properties
+ start end
+ '(font-lock-fontified t fontified t font-lock-multiline t))
+ (set-buffer-modified-p modified)))))
+
+;; Although I'm usually not a fan of treating non-printable ASCII like this, the
+;; lines act as sort more of a cue for me to use <C-x [> and <C-x ]> than a
+;; plain ol' '^L'.
+
+(use-package page-break-lines
+ :demand t
+ :config
+ (global-page-break-lines-mode))
diff --git a/lisp/appearance/modeline.el b/lisp/appearance/modeline.el
new file mode 100644
index 0000000..296a4f5
--- /dev/null
+++ b/lisp/appearance/modeline.el
@@ -0,0 +1,87 @@
+;;; modeline.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; My custom modeline for Emacs, which seeks to fit the dual role of a
+;;; functional status indicator for Emacs, as well as a "bar" in the sense of a
+;;; traditional tiling window manager.
+;;;
+
+;; My implementation of a clock for the modeline. The modeline is refreshed by a
+;; timer to ensure that it doesn't go too stale, as it could theoretically go
+;; several minutes without being updated.
+
+(defun mode-line-time ()
+ (format-time-string "%Y-%m-%d %H:%M"))
+
+(let ((timer (run-at-time t 60.0 #'ignore)))
+ (timer-set-function timer
+ (lambda ()
+ (force-mode-line-update))))
+
+;; For some time, I was using my own implementation of a modeline battery
+;; monitor. However, the constant calls to `battery-linux-sysfs' were
+;; interfering with the way that EXWM handles input focus, and I've since moved
+;; towards reusing `display-battery-mode'. Because strings in `mode-line-format'
+;; can contain control sequences, the percent symbol has to be escaped twice --
+;; once for `battery-format', and once for `mode-line-format'.
+;;
+;; Obviously, my tower lacks a battery, so I only enable this on my laptop.
+
+(when (string= (system-name) "Upsilon")
+ (require 'battery)
+ (display-battery-mode +1)
+ (setq battery-mode-line-format "%L: %t, %p%%%%"))
+
+(defun maybe-elide (s &optional max-length)
+ "Truncate `S' to `MAX-LENGTH'.
+Return a string derived from `S', possible elided, that is at
+most 1MAX-LENGTH characters long."
+ (let ((max-length (or max-length 30)))
+ (if (> (length s) max-length)
+ (concat (substring s 0 (- max-length 4)) " ...")
+ s)))
+
+(defun escape-mode-line-construct (s)
+ "Sanitize `S' for use in `mode-line-format'.
+Ensure that no part of S will be interpreted as a modeline format
+specifier."
+ (replace-regexp-in-string (regexp-quote "%") "%%" s t t))
+
+(defun mode-line-render (left right)
+ "Return a string of `window-width' length containing LEFT, and
+RIGHT aligned respectively."
+ (let* ((right (format-mode-line right))
+ (left (maybe-elide (format-mode-line left)
+ (- (window-width) (length right) 1)))
+ (padding (- (window-width) (length left) (length right) 1))
+ (padding (if (wholenump padding) (make-string padding ?\ ))))
+ (if padding
+ (escape-mode-line-construct (concat " " left padding right))
+ (concat " " left right))))
+
+(defun remove-unicode (s)
+ "Remove non-ASCII or non-printable ASCII characters from `S'."
+ (apply #'string (seq-filter #'(lambda (c) (and (>= c ? ) (<= c ?~))) s)))
+
+(setq-default mode-line-format
+ '((:eval
+ (mode-line-render
+ (cond ((eq major-mode 'exwm-mode)
+ (concat "%b: " (remove-unicode exwm-title)))
+ ((eq major-mode 'pdf-view-mode)
+ (concat "%b ["
+ (number-to-string (pdf-view-current-page))
+ "/"
+ (number-to-string (pdf-cache-number-of-pages))
+ "]"))
+ (t
+ (concat "%b [%m] [%*] Ln: %l/"
+ (number-to-string (1+ (count-lines 1 (point-max))))
+ " Col: %c")))
+ (cond ((string= (system-name) "Upsilon")
+ (format "%s | %s"
+ (mode-line-time)
+ battery-mode-line-string))
+ (t (mode-line-time)))))))
diff --git a/lisp/appearance/theme.el b/lisp/appearance/theme.el
new file mode 100644
index 0000000..10b5624
--- /dev/null
+++ b/lisp/appearance/theme.el
@@ -0,0 +1,251 @@
+;;; theme.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; This is my custom theme, the code for which is very loosely adapted from
+;;; moe-theme.el [1], but most of the palette has been altered to emulate the
+;;; appearance of my old setup of Emacs in a terminal emulator.
+;;;
+;;; [1]: https://github.com/kuanyui/moe-theme.el
+
+(deftheme chayote
+ "Inspired by the appearance of Emacs in my terminal emulator.")
+
+;; The colors have been extracted into a variable, `palette', and a macro,
+;; `with-palette', to provide access to the theme's color definitions without
+;; significantly polluting the global namespace. To help me in modifying the
+;; theme, I've defined a function similar to `list-colors-display', but for
+;; showing the colors in the palette I've defined.
+
+(defvar palette
+ '((yellow-1 "#ffd787") (yellow-2 "#ffd700") (yellow-3 "#c4a000")
+ (yellow-3-5 "#aaaa11") (yellow-4 "#875f00")
+
+ (orange-1 "#ffaf87") (orange-2 "#ff8700") (orange-3 "#ff5d17")
+ (orange-4 "#d75f00") (orange-5 "#af5f00")
+
+ (magenta-1 "#ff7bbb") (magenta-2 "#ff4ea3") (magenta-3 "#ff1f8b")
+
+ (green-1 "#87ff87") (green-2 "#a1db00") (green-3 "#5faf00")
+ (green-4 "#008700") (green-5 "#005f00")
+
+ (cyan-1 "#afd7d7") (cyan-2 "#87ffd7") (cyan-3 "#00d7af")
+ (cyan-4 "#00ac8a") (cyan-5 "#5faf87") (cyan-6 "#005f5f")
+ (cyan-7 "#236f73")
+
+ (blue-1 "#1fb6d3") (blue-2 "#1f5bff") (blue-3 "#005f87")
+ (blue-4 "#5f7ab1") (blue-5 "#0000af") (blue-6 "#00005f")
+
+ (purple-1 "#d18aff") (purple-2 "#af5fff") (purple-3 "#9a08ff")
+ (purple-4 "#6c0099")
+
+ (red-1 "#ef2929") (red-2 "#dd0000") (red-3 "#a40000")
+ (red-4 "#5f0000")
+
+ (white-1 "#f0e9dc") (white-2 "#c6c6c6") (white-3 "#b2b2b2")
+
+ (black-1 "#a8a8a8") (black-2 "#8a8a8a") (black-2-5 "#6c6c6c")
+ (black-3 "#4e4e4e") (black-4 "#3a3a3a") (black-5 "#303539")
+ (black-6 "#000000")
+
+ (LIGHT_BG "#fdfde7") (white-0 "#eeeeee")
+
+ (green-02 "#5fd700") (green-01 "#d7ff00") (green-0 "#d7ff5f")
+ (green-00 "#d7ff87")
+
+ (cyan-0 "#d7ffd7")
+
+ (blue-01 "#c3c9f8") (blue-0 "#afd7ff") (blue-00 "#d7d7ff")
+
+ (yellow-0 "#ffff87") (yellow-00 "#ffffaf")
+
+ (purple-0 "#af87ff") (purple-00 "#e6a8df")
+
+ (red-0 "#ff4b4b") (red-00 "#ffafaf")
+
+ (magenta-0 "#ffafd7") (magenta-00 "#ffd7ff")
+
+ (orange-0 "#ffaf87") (orange-00 "#ffd787") (orange-000 "#ffd7af")
+
+ (linum-dark "#87875f") (linum-light "#d7d7af"))
+ "The palette of colors in use with my 'chayote' theme.")
+
+(defmacro with-palette (&rest form)
+ "Evaluate `FORM' in a `let' containing the theme's colors."
+ `(let ((class '((class color) (min-colors 89)))
+ ,@palette)
+ ,@form))
+
+(defun display-palette ()
+ "Display names of colors in `PALETTE', and show what they look like."
+ (interactive)
+ (with-help-window "*Chayote Colors*"
+ (with-current-buffer standard-output
+ (erase-buffer)
+ (dolist (color palette)
+ (let* ((name (symbol-name (first color)))
+ (value (second color)))
+ (put-text-property 0 (length name)
+ 'font-lock-face `(:foreground ,value)
+ name)
+ (insert name)
+ (insert (propertize " " 'display '(space :align-to (- right 9))))
+ (insert " ")
+ (put-text-property 0 (length value)
+ 'font-lock-face `(:background ,value)
+ value)
+ (insert value)
+ (terpri)))
+ (set-buffer-modified-p nil)
+ (setq truncate-lines t))))
+
+(with-palette
+ (custom-theme-set-faces
+ 'chayote
+ `(default ((,class (:foreground ,white-1 :background ,black-5 :family "Terminus" :height 90))))
+ `(italic ((,class (:italic t))))
+ `(cursor ((,class (:background ,white-0))))
+ `(variable-pitch ((,class (:inherit default))))
+
+ `(link ((,class (:foreground ,blue-1 :underline t))))
+
+ ;; Highlighting faces
+ `(highlight ((,class (:background ,black-3))))
+ `(hl-line ((,class (:background ,black-4))))
+ `(highlight-symbol-face ((,class (:background ,green-5))))
+ `(region ((,class (:background ,blue-4))))
+ `(secondary-selection ((,class (:background ,blue-3 :foreground ,white-0))))
+ `(isearch ((,class (:foreground ,white-1 :background ,orange-3))))
+ `(lazy-highlight ((,class (:background ,magenta-3 :foreground ,white-1))))
+ `(trailing-whitespace ((,class (:background ,red-3))))
+ `(show-paren-match ((,class (:background ,blue-3 :foreground nil))))
+ `(header-line ((,class (:background ,blue-3 :foreground ,white-0))))
+ `(help-argument-name ((,class (:foreground ,magenta-1 :italic t))))
+ `(eldoc-highlight-function-argument ((,class (:foreground ,green-01 :bold t :underline t :background ,green-5))))
+
+ ;; Mode line & frames' faces
+ `(mode-line ((,class (:box nil :background ,white-1 :foreground ,black-5))))
+ `(mode-line-inactive ((,class (:background ,white-3 :foreground ,black-5))))
+ `(mode-line-buffer-id ((,class (:box nil :foreground ,black-5 :background nil))))
+ `(vertical-border ((,class (:foreground ,black-3 :background ,black-3))))
+
+ ;; Escape and prompt faces
+ `(minibuffer-prompt ((,class (:foreground ,blue-1))))
+ `(escape-glyph ((,class (:foreground ,yellow-3))))
+ `(error ((,class (:foreground ,red-0))))
+ `(warning ((,class (:foreground ,orange-1))))
+ `(success ((,class (:foreground ,green-1))))
+
+ ;; Font lock faces
+ `(font-lock-builtin-face ((,class (:foreground ,cyan-1))))
+ `(font-lock-comment-delimiter-face ((,class (:inherit font-lock-comment-face))))
+ `(font-lock-comment-face ((,class (:foreground ,orange-2))))
+ `(font-lock-constant-face ((,class (:foreground ,cyan-2))))
+ `(font-lock-doc-face ((,class (:inherit font-lock-string-face))))
+ `(font-lock-doc-string-face ((,class (:inherit font-lock-string-face))))
+ `(font-lock-function-name-face ((,class (:foreground ,blue-0))))
+ `(font-lock-keyword-face ((,class (:foreground ,blue-1))))
+ `(font-lock-negation-char-face ((,class (:foreground ,red-0))))
+ `(font-lock-preprocessor-face ((,class (:inherit font-lock-builtin-face))))
+ `(font-lock-regexp-grouping-backslash ((,class (:foreground ,yellow-1))))
+ `(font-lock-regexp-grouping-construct ((,class (:foreground ,purple-1))))
+ `(font-lock-string-face ((,class (:foreground ,orange-1))))
+ `(font-lock-type-face ((,class (:foreground ,green-1))))
+ `(font-lock-variable-name-face ((,class (:foreground ,yellow-1))))
+ `(font-lock-warning-face ((,class (:weight bold :foreground ,red-2))))
+
+ ;; Info
+ `(Info-quoted ((,class (:inherit default))))
+
+ ;; company
+ `(company-tooltip ((,class (:background ,black-4))))
+ `(company-scrollbar-bg ((,class (:background ,black-4))))
+ `(company-scrollbar-fg ((,class (:background ,orange-2))))
+ `(company-tooltip-selection ((,class (:background ,black-3))))
+ `(company-tooltip-common ((,class (:foreground ,blue-1))))
+ `(company-tooltip-annotation ((,class (:foreground ,cyan-1))))
+
+ ;; AUCTeX
+ `(font-latex-sedate-face ((,class (:inherit font-lock-keyword-face))))
+
+ ;; Gnus
+ `(gnus-group-mail-3 ((,class (:foreground ,green-2))))
+
+ ;; org-mode
+ `(org-document-info-keyword ((,class (:foreground ,red-0))))
+ `(org-document-title ((,class (:inherit org-document-info-keyword))))
+ `(org-document-info ((,class (:inherit org-document-info-keyword))))
+ `(org-meta-line ((,class (:inherit org-document-info-keyword))))
+ `(org-archived ((,class (:slant italic))))
+ `(org-checkbox ((,class (:background ,white-2 :foreground ,black-3
+ :box (:line-width 1 :style released-button)))))
+ `(org-date ((,class (:foreground ,green-3 :underline t))))
+ `(org-done ((,class (:foreground ,green-4 :background ,green-00
+ :box (:line-width 1 :style none)))))
+ `(org-todo ((,class (:foreground ,red-3 :background ,orange-0
+ :box (:line-width 1 :style none)))))
+ `(org-level-1 ((,class (:foreground ,purple-00))))
+ `(org-level-2 ((,class (:foreground ,blue-0))))
+ `(org-level-3 ((,class (:foreground ,cyan-3))))
+ `(org-level-4 ((,class (:foreground ,green-02))))
+ `(org-level-5 ((,class (:foreground ,red-2))))
+ `(org-level-6 ((,class (:foreground ,orange-2))))
+ `(org-level-7 ((,class (:foreground ,magenta-2))))
+ `(org-level-8 ((,class (:foreground ,yellow-2))))
+ `(org-tag ((,class (:background ,black-1 :foreground ,white-0))))
+ `(org-column ((,class (:background ,yellow-00 :foreground ,black-3))))
+ `(org-column-title ((,class (:background ,blue-0 :foreground ,black-5 :underline t))))
+ `(org-agenda-structure ((,class (:foreground ,cyan-4 :background nil :bold nil))))
+ `(org-deadline-announce ((,class (:foreground ,red-3))))
+ `(org-scheduled ((,class (:foreground ,black-2))))
+ `(org-scheduled-previously ((,class (:foreground ,red-1))))
+ `(org-scheduled-today ((,class (:foreground ,blue-2))))
+ `(org-special-keyword ((,class (:foreground ,green-3))))
+ `(org-table ((,class (:background ,black-4 :foreground ,white-3))))
+ ;; `(org-time-grid ((,class (:foreground ,white-4))))
+ `(org-upcoming-deadline ((,class (:foreground ,red-3))))
+ ;; `(org-warning ((,class (:foreground ,white-0 :background ,red-3))))
+ `(org-warning ((,class (:foreground ,white-0 :background ,red-4))))
+ `(org-formula ((,class (:foreground ,purple-2))))
+ `(org-headline-done ((,class (:foreground ,green-2))))
+ `(org-hide ((,class (:foreground ,LIGHT_BG))))
+ `(org-code ((,class (:foreground ,blue-2 :background ,white-1))))
+ ;; `(org-link ((,class (:foreground ,blue-2 :underline ,blue-2))))
+ `(org-link ((,class (:inherit link))))
+ `(org-footnote ((,class (:foreground ,magenta-3))))
+ `(org-ellipsis ((,class (:foreground ,red-2))))
+ `(org-agenda-clocking ((,class (:foreground ,blue-3 :background ,blue-0))))
+ `(org-agenda-date ((,class (:foreground ,blue-1 :background nil :underline nil))))
+ `(org-agenda-date-weekend ((,class (:foreground ,purple-2 :background nil :slant italic :bold nil))))
+ `(org-agenda-date-today ((,class (:foreground ,blue-0 :background nil :underline t))))
+ `(org-agenda-column-dateline ((,class (:foreground ,white-0 :background ,yellow-0 :underline t))))
+ `(org-agenda-todo ((,class (:foreground ,white-0 :background ,red-2))))
+ `(org-agenda-done ((,class (:foreground ,green-3 :background ,nil))))
+ `(org-agenda-dimmed-todo-face ((,class (:foreground ,white-0 :background ,red-2))))
+ `(org-priority ((,class (:foreground ,red-1 :background ,nil))))
+ `(org-block ((,class (:foreground ,white-1 :background ,black-4 :extend t))))
+ `(org-block-begin-line ((,class (:foreground ,black-3))))
+ `(org-block-end-line ((,class (:foreground ,black-3))))
+ `(org-block-background ((,class (:background ,white-1))))
+ ;; `(org-block-begin-line ((,class (:foreground ,blue-3 :background ,blue-0))))
+ ;; `(org-block-end-line ((,class (:foreground ,white-2 :background ,white-1))))
+ `(org-quote ((,class (:foreground ,blue-3 :background ,blue-00))))
+ `(org-mode-line-clock ((,class (:foreground ,blue-3 :background ,blue-0 :bold t))))
+ `(org-mode-line-clock-overrun ((,class (:foreground ,white-0 :background ,red-1 :bold t))))
+ ;; `(org-verbatim ((,class (:foreground ,blue-3 :background ,blue-00 :bold nil))))
+ `(org-verbatim ((,class (:foreground ,blue-1 :background ,black-4))))
+
+ ;; erc
+ `(erc-input-face ((,class (:foreground ,white-1))))
+ `(erc-button ((,class (:inherit link))))
+
+ ;; smartparens
+ `(sp-pair-overlay-face ((,class (:inherit default)))))
+
+ (custom-theme-set-variables
+ 'chayote
+ `(ansi-color-names-vector [,black-5 ,red-0 ,green-0 ,yellow-1
+ ,blue-1 ,purple-1 ,blue-0 ,white-1])))
+
+(enable-theme 'chayote)
diff --git a/lisp/core.el b/lisp/core.el
new file mode 100644
index 0000000..34f9817
--- /dev/null
+++ b/lisp/core.el
@@ -0,0 +1,196 @@
+;;; core.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for the "core" behavior of GNU Emacs. Generally, anything
+;;; which does not involve a third-party package.
+;;;
+
+
+
+;; [HACK] Use the correct `emacsclient' binary on Gentoo.
+(when (cl-search "28" (emacs-version))
+ (setq with-editor-emacsclient-executable "emacsclient-emacs-28-vcs"))
+
+;; Having Emacs open to `*scratch*' feels as though it's inviting me to punch
+;; out some Lisp forms and evaluate them. I like to pretend that my computers
+;; are Lisp machines.
+
+;; TODO: Replace with dashboard.el?
+
+(setq inhibit-startup-message t
+ inhibit-startup-buffer-menu t
+ initial-scratch-message nil
+ initial-buffer-choice nil)
+
+;; A recurring theme in this configuration file is changing where Emacs packages
+;; drop their various state files. 'no-littering.el' is a package that handles
+;; this by creating 'var/' and 'etc/' directories in the Emacs directory, but
+;; it's implemented in a way that pollutes the global namespace with variables
+;; for packages that I don't use, and would require a significant amount of
+;; configuration on my part anyway, so I've decided to do this manually, setting
+;; relevant variables to subdirectories of XDG_CACHE_HOME and XDG_DATA_HOME.
+
+;; Save backup files to one directory instead of making a mess of the filesystem.
+
+(setq backup-directory-alist `(("." . "~/.cache/emacs")))
+
+;; `auto-save', in addition to the actual files it saves, maintains another file
+;; listing the files it's currently taking care of.
+
+(setq auto-save-list-file-prefix "~/.cache/emacs/auto-save-list/.saves-")
+
+;; Disable the creation of locking symlinks in the current directory. This is a
+;; very opinionated choice, and probably isn't a good idea for most.
+
+(setq create-lockfiles nil)
+
+;; I don't use 'custom.el' to set variables, but a few of the packages I use do.
+;; This snippet ensures that a massacre is not made of my init.el.
+
+(setq custom-file "~/.emacs.d/custom.el")
+(when (file-exists-p custom-file)
+ (load custom-file))
+
+;; I don't really care about persisting minibuffer history between sessions.
+;; Even if I disable `savehist-mode', it still writes to disk, so the least I
+;; can do is move the history file to somewhere that isn't .emacs.d.
+
+(savehist-mode -1)
+(setq savehist-file "~/.cache/emacs/history")
+
+(use-package tramp
+ :straight nil
+ :init
+ (setq tramp-backup-directory-alist backup-directory-alist)
+ (setq-default tramp-persistency-file-name "~/.cache/emacs/tramp"))
+
+(use-package nsm
+ :straight nil
+ :init
+ (setq nsm-settings-file "~/.cache/emacs/network-security.data"))
+
+;; Use a consistent confirmation dialog of "y or n".
+
+(fset 'yes-or-no-p 'y-or-n-p)
+
+;; Automatically revert buffers and dired listings when something on disk
+;; changes.
+
+(use-package autorevert
+ :config
+ (global-auto-revert-mode 1)
+ (setq global-auto-revert-non-file-buffers t
+ auto-revert-verbose nil))
+
+;; "Command attempted to use minibuffer while in minibuffer" gets old fast.
+
+(setq enable-recursive-minibuffers t)
+
+;; Select help windows when I pop them so that I can kill them with <q>.
+
+(setq help-window-select t)
+
+;; Most *NIX tools work best when files are terminated with a newline.
+
+(setq require-final-newline t)
+
+;; Sentences should be separated by a single space. Treat two sentences as such
+;; when filling.
+
+(setq sentence-end-double-space nil)
+
+;; I use the tab key, but I generally prefer space characters to tabstops and
+;; like a four-character width for indentation.
+
+(setq-default indent-tabs-mode nil
+ tab-width 4)
+
+;; Modern conventions state that 80 characters is the standard width.
+
+(setq-default fill-column 80)
+
+;; When killing, add whatever's in the system clipboard if it isn't there
+;; already, so I can get it back with <M-y>.
+
+(setq save-interprogram-paste-before-kill t)
+
+;; Enable the default-disabled functions that I use.
+
+(put 'downcase-region 'disabled nil)
+(put 'upcase-region 'disabled nil)
+
+;; Enable useful visual queues.
+
+(column-number-mode t)
+
+;; Unbind <C-z> and <C-x C-z> so that I'm not accidentally calling =suspend-frame=.
+
+(global-unset-key (kbd "C-z"))
+(global-unset-key (kbd "C-x C-z"))
+
+;; Replace `kill-word', `backward-kill-word', and `kill-visual-line' with
+;; equivalents that do not copy the region into the kill ring.
+
+(defun delete-word (arg)
+ "Delete characters forward until encountering the end of a word.
+With argument ARG, do this that many times."
+ (interactive "p")
+ (delete-region (point) (progn (forward-word arg) (point))))
+
+(defun backward-delete-word (arg)
+ "Delete characters backward until encountering the beginning of a word.
+With argument ARG, do this that many times."
+ (interactive "p")
+ (delete-word (- arg)))
+
+(defun delete-visual-line (&optional arg)
+ "Delete the rest of the visual line.
+With prefix argument ARG, delete that many visual lines from point.
+If ARG is negative, delete visual lines backward.
+If ARG is zero, delete the text before point on the current visual
+line."
+ (interactive "P")
+ ;; Like in `kill-line', it's better to move point to the other end
+ ;; of the kill before killing.
+ (let ((opoint (point))
+ (delete-whole-line (and kill-whole-line (bolp))))
+ (if arg
+ (vertical-motion (prefix-numeric-value arg))
+ (end-of-visual-line 1)
+ (if (= (point) opoint)
+ (vertical-motion 1)
+ ;; Skip any trailing whitespace at the end of the visual line.
+ ;; We used to do this only if `show-trailing-whitespace' is
+ ;; nil, but that's wrong; the correct thing would be to check
+ ;; whether the trailing whitespace is highlighted. But, it's
+ ;; OK to just do this unconditionally.
+ (skip-chars-forward " \t")))
+ (delete-region opoint (if (and delete-whole-line (= (following-char) ?\n))
+ (1+ (point))
+ (point)))))
+
+(bind-key "M-d" #'delete-word)
+(bind-key "M-DEL" #'backward-delete-word)
+(bind-key "C-k" #'delete-visual-line)
+
+;; I use <M-v> and <M-V> for movement, mostly, so having <prior> and <next>
+;; bound to the same functions is a little redundant. What I prefer is to bind
+;; them to functions for scolling by one line at a time instead. Sometimes
+;; useful when perusing IRC logs.
+
+(define-minor-mode baby-scroll-mode
+ "Toggle `baby-scroll-mode'.
+Minor mode which remaps <next> and <prior> to less extreme
+scrolling functions."
+ :global t
+ nil "Baby Scroll" '(([next] . scroll-up-line)
+ ([prior] . scroll-down-line)))
+
+(baby-scroll-mode)
+
+;; Don't do jerky jumps when the cursor reaches the end of the window. Instead,
+;; just scroll by one line.
+
+(setq scroll-conservatively most-positive-fixnum)
diff --git a/lisp/documents.el b/lisp/documents.el
new file mode 100644
index 0000000..88d3ba2
--- /dev/null
+++ b/lisp/documents.el
@@ -0,0 +1,333 @@
+;;; documents.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of packages for working with various kinds of text documents.
+;;; Spell-checking, and the like.
+;;;
+
+
+
+;; Ensure that my Aspell "personal dictionary" is included when invoking ispell.
+
+(setq ispell-personal-dictionary "~/.aspell.en.pws")
+
+
+;;;
+;;; Org
+;;;
+
+(use-package org
+ :straight org-plus-contrib
+
+ ;; File system of hygene.
+ :init
+ (setq-default org-id-locations-file "~/.cache/emacs/org-id-locations"
+ org-preview-latex-image-directory "~/.cache/emacs/ltximg/"
+ org-preview-latex-image-directory "~/.cache/emacs/ltximg/")
+
+ ;; General appearance and quality-of-life settings.
+ :init
+ (setq org-return-follows-link t
+ org-startup-folded nil
+ org-cycle-separator-lines -1
+ org-startup-with-inline-images t
+ org-hide-emphasis-markers t
+ org-tags-column 100
+ org-yank-folded-subtrees nil
+ org-link-elisp-confirm-function nil)
+
+ ;; Keep everything in column zero, instead of indenting according to the depth
+ ;; of the preceding headline.
+
+ :init
+ (setq org-adapt-indentation nil)
+
+ ;; Have <tab> indent according to the indentation rules of the corresponding
+ ;; mode in an =org-src= block, and start indentation on column 0. Taken from
+ ;; [1] and [2].
+ ;;
+ ;; [1]: https://stackoverflow.com/questions/15773354/indent-code-in-org-babel-src-blocks
+ ;; [2]: https://emacs.stackexchange.com/questions/9472/org-mode-source-block-doesnt-respect-parent-buffer-indentation
+ :init
+ (setq org-src-tab-acts-natively t
+ org-edit-src-content-indentation 0)
+
+ ;; Reuse the current window when I punch in <C-c '>, rather than mess up my
+ ;; window configuration.
+ :init
+ (setq org-src-window-setup 'current-window)
+
+ ;; Enable Geiser evaluation for Scheme blocks.
+ :config
+ (org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((scheme . t)))
+
+ ;; Scale up embedded LaTeX. The default size is unreadable.
+ :config
+ (setq org-format-latex-options
+ (plist-put org-format-latex-options :scale 1.5))
+
+ ;; Don't interpret text following underscores as subscript text unless it is
+ ;; enclosed in braces.
+ :init
+ (setq org-use-sub-superscripts "{}")
+
+ ;; Enable Yasnippet and Flyspell.
+ :hook
+ (org-mode . yas-minor-mode-on)
+ (org-mode . flyspell-mode)
+
+ ;; Replace my Imenu binding with `org-goto,' as it allows me to select parent
+ ;; headings.
+ :init
+ (setq org-goto-interface 'outline-path-completionp)
+ (setq org-outline-path-complete-in-steps nil)
+
+ :bind
+ (:map org-mode-map ("C-c s" . org-goto))
+
+ ;; Global keybindings for my Org workflow.
+ :bind
+ ("C-c a" . (lambda () (interactive) (org-agenda nil "d")))
+ ("C-c c" . (lambda () (interactive) (org-capture nil "c")))
+ ("C-c t" . (lambda () (interactive) (org-capture nil "t")))
+
+ ;; Use Counsel's functionality for adding tags.
+ :bind
+ (:map org-mode-map ("C-c C-q" . counsel-org-tag))
+
+ ;; I maintain a personal wiki using Org and I like to link between pages. This
+ ;; is just a convenience link type, rooted at the base of my notes folder so
+ ;; that I can easily refer to other pages from subdirectories of
+ ;; `notes-directory'.
+ :config
+ (defvar notes-directory "~/Notes"
+ "Where all the pages of my 'personal wiki' are stored.")
+
+ (defun org-notes-follow (link)
+ "Open LINK, an Org link from a notes buffer.
+Visits LINK, or if LINK names a subdirectory of
+`notes-directory', the 'Index.org' in LINK."
+ (let ((directory-p (file-directory-p (expand-file-name link notes-directory))))
+ (find-file (expand-file-name (if directory-p "Index.org" (concat link ".org"))
+ notes-directory))))
+
+ (org-link-set-parameters "note" :follow #'org-notes-follow)
+
+ ;; Code for interactively navigating the personal wiki.
+ :config
+ (defvar notes-directory-ignore
+ '("." ".." ".git" ".gitignore" ".strfolder" "Drill" "GTD")
+ "Files and directories in `notes-directory' to exclude from
+ wiki interactions.")
+
+ (defun notes-file-is-page (filename)
+ "Returns whether or not FILENAME names a page rather than a category."
+ (string= (cl-subseq filename (- (length filename) 4)) ".org"))
+
+ (defun notes-file-should-ignore (filename)
+ "Returns whether or not a file in `notes-directory' should be ignored.
+ Members of `notes-directory-ignore' are ignored."
+ (member filename notes-directory-ignore))
+
+ (defun notes-hierarchy-paths (&optional category)
+ "Returns page and category paths relative to `notes-directory'.
+ The resultant list does not include files that satisfy the
+ `notes-file-should-ignore' predicate."
+ (let* ((path (if category
+ (concat notes-directory "/" category)
+ notes-directory))
+ (contents (cl-remove-if #'notes-file-should-ignore
+ (directory-files path)))
+ (pages (cl-remove-if-not #'notes-file-is-page contents))
+ (categories (cl-remove-if-not #'(lambda (subcategory)
+ (file-directory-p (concat path "/" subcategory)))
+ contents))
+ (categories (if category
+ (map 'list
+ #'(lambda (subcategory) (concat category "/" subcategory))
+ categories)
+ categories)))
+ (append (if category
+ (map 'list #'(lambda (page) (concat category "/" page)) pages)
+ pages)
+ categories
+ (apply #'append (map 'list #'notes-hierarchy-paths categories)))))
+
+ (defun notes-hierarchy ()
+ "Returns every page and category in `notes-directory'.
+ The result contains lists of the form (NAME PATH IS-PAGE-P), and
+ does not include files that satisfy the
+ `notes-file-should-ignore' predicate."
+ (map 'list
+ #'(lambda (path) (list
+ (cond ((cl-search "Index.org" path)
+ (let ((name (cl-subseq path 0 -9)))
+ (if (string= name "")
+ "/"
+ name)))
+ ((notes-file-is-page path) (cl-subseq path 0 -4))
+ (t path))
+ (concat notes-directory "/" path)
+ (notes-file-is-page path)))
+ (notes-hierarchy-paths)))
+
+ (defun notes-page-goto ()
+ "Interactively find a page in `notes-directory'."
+ (interactive)
+ (let* ((hierarchy (notes-hierarchy))
+ (hierarchy (cl-remove-if-not #'third hierarchy))
+ (name (ivy-read "Find file: " (map 'list #'first hierarchy)))
+ (item (cl-find-if #'(lambda (item) (string= name (first item))) hierarchy)))
+ (if item
+ (find-file (second item))
+ (let ((path (concat notes-directory "/" name ".org")))
+ (find-file path)))))
+
+ ;; Set up a key binding for quickly querying the wiki.
+ :bind
+ ("C-c w" . notes-page-goto)
+
+ ;; Low-friction means of adding things to my GTD inbox.
+ :config
+ (setq org-capture-templates
+ `(("t" "TODO" entry (file "~/Notes/GTD/Inbox.org")
+ "* TODO %?\n")))
+
+
+ ;; A rather convoluted agenda setup to show me:
+ ;; - The tasks I've marked as needing to be done today.
+ ;; - The inbox items I need to process.
+ ;; - My daily habits (implemented as recurring Org TODO's).
+ ;; - A view of deadlines and scheduled items for the week ahead.
+ :init
+ (setq org-agenda-files '("~/Notes/GTD/Habits.org"
+ "~/Notes/GTD/Inbox.org"
+ "~/Notes/GTD/Actionable.org"))
+
+ (let ((entry-done
+ '(lambda () (org-agenda-skip-entry-if 'todo 'done)))
+
+ (subtree-is-habit
+ '(lambda ()
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (if (string= (org-entry-get nil "STYLE") "habit")
+ subtree-end
+ nil))))
+
+ (subtree-is-not-habit
+ '(lambda ()
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (if (not (string= (org-entry-get nil "STYLE") "habit"))
+ subtree-end
+ nil))))
+
+ (subtree-is-not-inbox
+ '(lambda ()
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (if (not (cl-search "Inbox.org" (buffer-name)))
+ subtree-end
+ nil)))))
+
+ (setq org-agenda-custom-commands
+ `(("d" "Daily"
+ ((tags "PRIORITY=\"A\""
+ ((org-agenda-skip-function ,entry-done)
+ (org-agenda-overriding-header "Tasks for Today:")))
+ (alltodo ""
+ ((org-agenda-skip-function ,subtree-is-not-inbox)
+ (org-agenda-overriding-header "Inbox:")))
+ (agenda ""
+ ((org-agenda-span 'day)
+ (org-agenda-skip-function ,subtree-is-not-habit)
+ (org-deadline-warning-days 0)
+ (org-agenda-overriding-header "Habits:")))
+ (agenda ""
+ ((org-agenda-span 'week)
+ (org-agenda-skip-function ,subtree-is-habit)
+ (org-agenda-overriding-header "Week Ahead:"))))
+ ((org-agenda-compact-blocks t)))))))
+
+(use-package org-drill
+ ;; org-drill in upstream Org is unmaintained, and as described in [1], no
+ ;; longer works with the latest version of Org. I'm was previously using this
+ ;; fork [2], but the maintainer has since begun working on another fork [3]
+ ;; which is where I am now.
+ ;;
+ ;; [1]: https://bitbucket.org/eeeickythump/org-drill/issues/62/org-drill-doesnt-work-with-org-mode-92
+ ;; [2]: https://bitbucket.org/eeeickythump/org-drill/issues/63/maintainership
+ ;; [3]: https://gitlab.com/phillord/org-drill/-/blob/master/org-drill.el
+ :straight (org-drill :type git :host gitlab :repo "phillord/org-drill")
+
+ ;; Update LaTeX fragments when showing them to me.
+ :init
+ (with-eval-after-load 'org-drill
+ (defun org-drill--show-latex-fragments ()
+ (org-latex-preview '(16))))
+
+ ;; Scan the directory of the current buffer's file for other items, rather
+ ;; than just the current buffer. This is so that I can spread my cards across
+ ;; multiple files and still be tested on all of them when I run `org-drill'.
+ ;; The idea of combining topic during review sessions is suggested in Michael
+ ;; Nielsen's article [1].
+ ;;
+ ;; [1]: http://augmentingcognition.com/ltm.html
+ :init
+ (setq org-drill-scope 'directory)
+
+ ;; Use the SM-2 algorithm rather than the default SM-5 algorithm. I have faith
+ ;; in Anki's analysis of SM-5 [1] in the form that it is available.
+ ;;
+ ;; [1]: https://apps.ankiweb.net/docs/manual.html#what-about-sm-5
+ :init
+ (setq org-drill-spaced-repetition-algorithm 'sm2)
+
+ ;; Shut up the nagging "are you studying enough!?" message if I don't get
+ ;; enough items right. It's unhelpful and discourages me from being honest
+ ;; with the SRS algorithm.
+ :init
+ (setq org-drill-forgetting-index 100)
+
+ ;; Set up a keybinding for getting quickly starting a drill session.
+ :bind
+ ("<f11>" . (lambda ()
+ (interactive)
+ (let* ((drill-directory (concat notes-directory "/" "Drill"))
+ (path (-first #'(lambda (path) (s-ends-with-p ".org" path))
+ (directory-files drill-directory t))))
+ (find-file path)
+ (org-drill)))))
+
+;; The org-download package is incredible for quickly pull images into Org
+;; documents.
+
+(use-package org-download
+ ;; Configure it to use 'scrot' and the 'org-attach' machinery.
+ :init
+ (setq org-download-screenshot-method "scrot -s %s")
+ (setq org-download-method 'attach))
+
+;; When exporting Org as HTML, include syntax highlighting.
+
+(use-package htmlize
+ :config
+ (setq org-html-htmlize-output-type 'css))
+
+
+;;;
+;;; PDF
+;;;
+
+;; `doc-view' is unbearable for reading PDF's of any considerable size and
+;; `pdf-tools' brings a number of significant improvements, so I can justify not
+;; using the built-in solution. Even though I can run MuPDF in an EXWM buffer,
+;; the integration in terms of searching and viewing attachments that comes with
+;; pdf-tools makes for a much more pleasant experience.
+
+(use-package pdf-tools
+ :demand t
+ :config
+ (pdf-loader-install))
diff --git a/lisp/irc.el b/lisp/irc.el
new file mode 100644
index 0000000..670ceae
--- /dev/null
+++ b/lisp/irc.el
@@ -0,0 +1,214 @@
+;;; irc.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of ERC, as well as additional custom defuns for making an
+;;; enjoyable IRC experience within Emacs.
+;;;
+
+
+
+;; Functions for connecting to and authenticating with my IRC bouncer.
+
+(defun read-bouncer-password ()
+ "Pull the password for my bouncer from authinfo"
+ (let ((auth (auth-source-search :host "jakob.space" :user "jakob" :port 6697)))
+ (unless auth
+ (error "No auth entry for bouncer."))
+ (let ((secretf (plist-get (car auth) :secret)))
+ (unless secretf
+ (error "No secret for bouncer's auth entry."))
+ (funcall secretf))))
+
+(defvar *bouncer-port-alist* '(("freenode" . 6697)
+ ("tildetown" . 6671)))
+
+(defun connect-bouncer (server)
+ "Connect to `SERVER' on the bouncer and rename the buffer"
+ (let* ((port (cdr (assoc server *bouncer-port-alist*)))
+ (buf (erc-tls :server "jakob.space"
+ :port port
+ :password (read-bouncer-password))))
+ (erc-with-buffer (buf (rename-buffer server)))))
+
+;; Interactive function for connecting to all of my bouncer's IRC servers.
+
+(defvar irc-autoconnect-networks '("freenode" "tildetown")
+ "List of networks to automatically connect to when 'irc' is invoked.")
+
+(defun irc ()
+ "Connect to every network in `irc-autoconnect-networks'."
+ (interactive)
+ (dolist (network irc-autoconnect-networks)
+ (connect-bouncer network)))
+
+(defun irc-quit ()
+ "Disconnects from and kills all buffers associated with ERC."
+ (interactive)
+ (let ((server-buffers (cl-remove-if-not #'erc-server-buffer-p (erc-buffer-list))))
+ (dolist (buffer server-buffers)
+ (erc-with-buffer (buffer)
+ (erc-quit-server (funcall erc-quit-reason nil))))))
+
+(use-package erc
+ :straight nil
+
+ ;; Only load the modules I actually use. This list deviates from the default
+ ;; by including truncate, but omitting autojoin.
+ :config
+ (setq erc-modules '(button completion fill
+ irccontrols list match
+ menu move-to-prompt netsplit
+ networks noncommands readonly
+ ring stamp track truncate))
+ (erc-update-modules)
+
+ ;; Configure personal details.
+ :init
+ (setq erc-nick '("jakob" "tsarfox")
+ erc-user-full-name "Jakob <http://jakob.space>"
+ erc-quit-reason (lambda (reason)
+ (or reason (concat "Program received signal "
+ "SIGSEGV, Segmentation fault."))))
+
+ ;; Display the current nick in the prompt.
+ :init
+ (setq erc-prompt (lambda () (format "%s>" (erc-current-nick))))
+
+ ;; Center everything.
+ :init
+ (setq erc-fill-function 'erc-fill-static
+ erc-fill-column 100
+ erc-fill-static-center 22)
+
+ ;; Enable MIRC and control sequences so I can partake in figlet spam,
+ ;; objectively the best part of the IRC experience.
+ :init
+ (setq erc-interpret-controls-p t
+ erc-interpret-mirc-color t)
+
+ ;; Enable timestamps, and show them to the left of the messages.
+ :init
+ (setq erc-hide-timestamps nil
+ erc-insert-timestamp-function 'erc-insert-timestamp-left)
+
+ ;; Although 'log' is omitted from `erc-modules', I still explicitly disable
+ ;; logging. I can just save the buffer if I want to save a conversation.
+ :init
+ (setq erc-enable-logging nil
+ erc-save-buffer-on-part nil)
+
+ ;; Similarly, 'autowaway' is omitted from `erc-modules', but I'm disabling it
+ ;; anyway.
+ :init
+ (setq erc-auto-set-away nil
+ erc-auto-discard-away nil)
+
+ ;; Kill buffers when I disconnect.
+ :init
+ (setq erc-kill-buffer-on-part t
+ erc-kill-queries-on-quit t
+ erc-kill-server-buffer-on-quit t)
+
+ ;; Don't get in my way by popping new windows when someone queries me, and
+ ;; don't assault me with windows when I connect to my bouncer.
+ :init
+ (setq erc-auto-query 'bury
+ erc-join-buffer 'bury)
+
+ ;; Show CTCP requests. Not for any reason in particular, I just like seeing
+ ;; when people ping me.
+ :init
+ (setq erc-paranoid t)
+
+ ;; Hide JOIN/PART/QUIT messages from idlers.
+ :init
+ (setq erc-lurker-hide-list '("JOIN" "PART" "QUIT")
+ erc-lurker-threshold-time 3600)
+
+ ;; Configure `erc-track' to ignore the less interesting message types.
+ :init
+ (setq erc-track-exclude-server-buffer t
+ erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE"
+ "324" "329" "332" "333" "353" "477"))
+
+ ;; Enable channel tracking.
+ :config
+ (erc-track-minor-mode 1)
+ (erc-track-mode 1)
+
+ ;; Move to the prompt as soon as I begin typing. I've configured the buffer
+ ;; text preceding it to be read-only, anyway.
+ :config
+ (erc-move-to-prompt-enable)
+
+ ;; Bind a key for resetting =erc-track=, since I tend to get absolutely
+ ;; bombarded upon reconnecting.
+ :bind
+ (:map erc-mode-map
+ ("C-c f" . (lambda ()
+ (interactive)
+ (setq erc-modified-channels-alist nil)
+ (erc-modified-channels-update))))
+
+ ;; Let `erc-fill' handle filling.
+ :hook
+ (erc-mode . (lambda () (auto-fill-mode 0))))
+
+;; Configure 'erc-hl-nicks' to show names using pretty colors.
+
+(use-package erc-hl-nicks
+ :after erc
+ :config
+ (erc-hl-nicks-enable))
+
+;; Display a bar delimiting read and unread messages. Adapted from [1].
+;;
+;; [1]: https://www.emacswiki.org/emacs/ErcBar
+
+(defvar erc-bar-overlay nil
+ "Overlay used to set bar")
+
+(defvar erc-bar-threshold 1
+ "The minimum number of messages required for the bar to be
+displayed.")
+
+(defun erc-bar-move-back (n)
+ "Moves back n message lines. Ignores wrapping, and server
+messages."
+ (interactive "nHow many lines ? ")
+ (re-search-backward "^.*<.*>" nil t n))
+
+(defun erc-bar-update-overlay ()
+ "Update the overlay for current buffer, based on the content of
+erc-modified-channels-alist. Should be executed on window
+change."
+ (interactive)
+ (let* ((info (assq (current-buffer) erc-modified-channels-alist))
+ (count (cadr info)))
+ (if (and info (> count erc-bar-threshold))
+ (save-excursion
+ (goto-char (point-max))
+ (when (erc-bar-move-back count)
+ (let ((inhibit-field-text-motion t))
+ (move-overlay erc-bar-overlay
+ (line-beginning-position)
+ (line-end-position)
+ (current-buffer)))))
+ (delete-overlay erc-bar-overlay))))
+
+(with-eval-after-load 'erc
+ ;; Run the hook before 'erc-modified-channels-update
+ (defadvice erc-track-mode (after erc-bar-setup-hook
+ (&rest args) activate)
+ (remove-hook 'window-configuration-change-hook 'erc-bar-update-overlay)
+ (add-hook 'window-configuration-change-hook 'erc-bar-update-overlay))
+
+ (add-hook 'erc-send-completed-hook (lambda (str) (erc-bar-update-overlay))))
+
+;; Display the bar as a highlight on the most recently unread message.
+
+(with-palette
+ (setq erc-bar-overlay (make-overlay 0 0))
+ (overlay-put erc-bar-overlay 'face `(:background ,black-2)))
diff --git a/lisp/jakob.el b/lisp/jakob.el
new file mode 100644
index 0000000..f720d7e
--- /dev/null
+++ b/lisp/jakob.el
@@ -0,0 +1,81 @@
+;;; jakob.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Custom elisp written by yours truly that is independent of any particular
+;;; package.
+;;;
+
+
+;;;
+;;; Configuration.
+;;;
+
+;; Bind some keys for working with this configuration file.
+
+(defun config-goto ()
+ "Switch to a buffer visiting the config file."
+ (interactive)
+ (find-file "~/.emacs.d/init.el"))
+
+(bind-key "C-c e" #'config-goto)
+
+
+;;;
+;;; pass
+;;;
+
+;; TODO: Replace with pass.el?
+
+;; Here's a snippet for interactively pulling a password from password-store, so
+;; I'm not spawning a terminal every time I need to invoke my password manager.
+
+(defvar password-store-directory "~/.password-store"
+ "Directory containing passwords managed by `pass'.")
+
+(defun pass-password-files ()
+ "Returns the paths of every password managed by `pass'."
+ (let ((prefix-length (1+ (length password-store-directory)))
+ (suffix-length (length ".gpg")))
+ (map 'list
+ #'(lambda (path) (cl-subseq path prefix-length (- suffix-length)))
+ (directory-files-recursively password-store-directory ".gpg$"))))
+
+(defun pass ()
+ "Prompt for a password and copy it to the clipboard.
+Passwords are taken from `password-store-directory'."
+ (interactive)
+ (let* ((account (completing-read "Account: " (pass-password-files)))
+ (path (concat password-store-directory "/" account ".gpg")))
+ (with-temp-buffer
+ (insert-file-contents path)
+ (clipboard-kill-ring-save 1 (1- (buffer-end 1))))
+ (message "Password for %s copied to clipboard." account)))
+
+
+;;;
+;;; 8-ball
+;;;
+
+;; A little snippet for getting an answer to a dumb question. Responses were
+;; lifted from Wikipedia's page [1] about the toy.
+;;
+;; [1]: https://en.wikipedia.org/wiki/Magic_8-Ball
+
+(defun consult ()
+ "Consult the magic 8-ball.
+Interactive function to consult the magic 8-ball for answers to
+very important questions."
+ (interactive)
+ (let ((responses (list "It is certain." "It is decidedly so."
+ "Without a doubt." "Yes - definitely."
+ "You may rely on it." "As I see it, yes."
+ "Most likely." "Outlook good." "Yes."
+ "Signs point to yes." "Reply hazy, try again."
+ "Ask again later." "Better not tell you now."
+ "Cannot predict now." "Concentrate and ask again."
+ "Don't count on it." "My reply is no."
+ "My sources say no." "Outlook not so good."
+ "Very doubtful.")))
+ (message (elt responses (mod (random) (length responses))))))
diff --git a/lisp/languages/javascript.el b/lisp/languages/javascript.el
new file mode 100644
index 0000000..4ade3ff
--- /dev/null
+++ b/lisp/languages/javascript.el
@@ -0,0 +1,23 @@
+;;; javascript.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+
+
+(use-package js
+ :straight nil
+
+ ;; Set up `js-mode' to interact with a LSP daemon.
+ :requires lsp-mode
+ :hook (js-mode . lsp)
+ :config
+ (lsp-register-client
+ (make-lsp-client :new-connection (lsp-stdio-connection "~/.bin/js-ts-stdio")
+ :major-modes '(js-mode)
+ :server-id 'js-ts-stdio))
+
+;; I have yet to find a style guide I agree with, but one thing common to most
+;; of them that I _do_ agree with is that Javascript is far more readable with
+;; an indentation width of 2.
+ :init
+ (setq-default js-indent-level 2))
diff --git a/lisp/news.el b/lisp/news.el
new file mode 100644
index 0000000..df1cbf1
--- /dev/null
+++ b/lisp/news.el
@@ -0,0 +1,44 @@
+;;; news.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for using Emacs as a newsreader.
+;;;
+
+;; Most of my Gnus configuration is in gnus.el, which is not under version
+;; control because it contains a list of my accounts on various email servers.
+
+;; Stop Gnus from making a mess of my home directory.
+
+(setq gnus-init-file "~/.emacs.d/gnus.el"
+ gnus-startup-file "~/.local/share/newsrc"
+ gnus-read-newsrc-file nil
+ gnus-save-newsrc-file nil
+ gnus-directory "~/.local/share/gnus/news/"
+ message-directory "~/.local/share/gnus/mail")
+
+;; Sort articles such that newer items are at the top of the summary buffer.
+
+(setq gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-date))
+
+;; Enable Flyspell so I don't make a fool of myself when composing messages.
+
+(add-hook 'message-mode-hook #'flyspell-mode)
+
+;; Use w3m to display HTML emails. Adapted from [1].
+;;
+;; [1]: https://norman.walsh.name/2011/03/10/w3m
+
+(use-package w3m
+ :demand t
+ :config
+ (setq gnus-mime-display-multipart-related-as-mixed nil
+ mm-text-html-renderer #'w3m
+ mm-inline-text-html-with-images t
+ mm-inline-text-html-with-w3m-keymap nil
+ w3m-default-display-inline-images t))
+
+;; Encrypt emails such that we can read them as well.
+
+(setq mml-secure-openpgp-encrypt-to-self t)
diff --git a/lisp/programming.el b/lisp/programming.el
new file mode 100644
index 0000000..641a270
--- /dev/null
+++ b/lisp/programming.el
@@ -0,0 +1,157 @@
+;;; programming.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for "meta" packages which apply to all programming language
+;;; development environments I have configured.
+;;;
+
+(use-package dumb-jump
+ ;; Replace `xref' keybindings instead of using the default ones, since these
+ ;; will get overwritten if I'm using a proper tag indexer anyway.
+ :config
+ (define-key dumb-jump-mode-map (kbd "C-M-g") nil)
+ (define-key dumb-jump-mode-map (kbd "C-M-p") nil)
+ (define-key dumb-jump-mode-map (kbd "C-M-q") nil)
+ (define-key dumb-jump-mode-map (kbd "M-.") #'dumb-jump-go)
+ (define-key dumb-jump-mode-map (kbd "M-,") #'dumb-jump-back)
+ (dumb-jump-mode))
+
+(use-package company
+ ;; Start suggesting completions (almost) as soon as possible.
+ :config
+ (setq company-idle-delay 0.1
+ company-minimum-prefix-length 3)
+
+ ;; Use standard movement keys for selecting entries from the completion popup
+ ;; instead of those used for minibuffer next/previous.
+ :bind
+ (:map company-active-map
+ ("M-n" . nil)
+ ("M-p" . nil)
+ ("C-n" . company-select-next)
+ ("C-p" . company-select-previous))
+
+ ;; And have <tab> responsible for completion rather than <ret>, so that
+ ;; company isn't quite as in-the-way.
+ :bind
+ (:map company-active-map
+ ("TAB" . company-complete-selection)
+ ("<tab>" . company-complete-selection)
+ ("RET" . nil)
+ ("<return>" . nil)))
+
+(use-package compile
+ :straight nil
+ :bind
+ ("<f12>" . compile)
+
+ ;; Taken from EmacsWiki [1]. The compilation command can be changed by invoking
+ ;; compile with a prefix argument, so I don't see the advantage of popping a
+ ;; prompt every time.
+ ;;
+ ;; [1]: https://www.emacswiki.org/emacs/CompileCommand
+ :init
+ (setq compilation-read-command nil)
+
+ ;; Hide the compilation buffer upon successful compilation. I typically don't
+ ;; care about the output unless there were errors.
+ :config
+ (add-hook 'compilation-finish-functions
+ (lambda (buf strg)
+ (let ((win (get-buffer-window buf 'visible)))
+ (when win (delete-window win))))))
+
+(use-package flycheck)
+
+(use-package magit
+ :bind
+ ("C-c g" . magit-status))
+
+;; After several failed attempts at getting into Smartparens, I've finally
+;; developed a taste for it. Rommel was not lying when he said that "[i]t's like
+;; having cybernetic limbs—it makes you jump higher and punch harder."
+
+(use-package smartparens
+ ;; Use the default Smartparens configuration as a base.
+ :config
+ (require 'smartparens-config)
+
+ ;; Enable the Smartparens-flavored replacement for `show-paren-mode'.
+ :config
+ (show-smartparens-global-mode t)
+
+ ;; Enable Smartparens in all modes derived from `prog-mode' or `comint-mode',
+ ;; the latter hopefully covering all REPL cases.
+ :hook
+ (prog-mode . turn-on-smartparens-mode)
+ (comint-mode . turn-on-smartparens-mode)
+
+ ;; Rebind the default keys for manipulating sexps to the alternatives
+ ;; suggested in Emacs and Pairs [1].
+ ;;
+ ;; [1]: https://ebzzry.io/en/emacs-pairs/
+ :bind
+ (:map smartparens-mode-map
+ ("C-M-a" . sp-beginning-of-sexp)
+ ("C-M-e" . sp-end-of-sexp)
+ ("C-M-d" . sp-down-sexp)
+ ("C-M-u" . sp-backward-up-sexp)
+ ("C-M-f" . sp-forward-sexp)
+ ("C-M-b" . sp-backward-sexp)
+ ("C-M-n" . sp-next-sexp)
+ ("C-M-p" . sp-previous-sexp)
+
+ ("M-[" . sp-backward-unwrap-sexp)
+ ("M-]" . sp-unwrap-sexp)
+ ("C-<right>" . sp-forward-slurp-sexp)
+ ("C-<left>" . sp-backward-slurp-sexp)
+ ("M-<right>" . sp-foward-barf-sexp)
+ ("M-<right>" . sp-forward-barf-sexp)
+
+ ("C-M-t" . sp-transpose-sexp)
+ ("C-M-k" . sp-kill-sexp)
+ ("M-k" . sp-backward-kill-sexp))
+
+ ;; The following mask `split-line' and `completion-at-point', respectively,
+ ;; but I have yet to use either of these, so I don't mind using the keys for
+ ;; Smartparens. I use "o" as a mnemonic for "out", and "i" is a mnemonic for
+ ;; "in".
+ :bind
+ (:map smartparens-mode-map
+ ("C-M-o" . sp-up-sexp)
+ ("C-M-i" . sp-backward-down-sexp)))
+
+(use-package lsp-ui
+ ;; The only reason I'm using `lsp-ui' is for integration with Flycheck. The
+ ;; features like lenses don't interest me.
+ :init
+ (setq lsp-ui-sideline-enable nil
+ lsp-ui-peek-enable nil
+ lsp-ui-doc-enable nil))
+
+;; Disable the default machinery in `lsp-mode'. The reason I'm using the package
+;; in the first place is for its ability to integrate with the other
+;; programming-related packages I use.
+
+(use-package company-lsp
+ :commands company-lsp
+ :init
+ (setq company-lsp-cache-candidates 'auto))
+
+(use-package lsp-mode
+ :init
+ (setq lsp-prefer-flymake nil
+ lsp-enable-xref t)
+
+ ;; Filesystem hygene.
+ :init
+ (setq lsp-session-file "~/.cache/emacs/.lsp-session-v1"
+ lsp-auto-guess-root t)
+
+ ;; Use `xref' keybindings instead of the default ones.
+ :bind
+ (:map lsp-mode-map
+ ("M-." . lsp-find-definition)
+ ("M-," . pop-tag-mark)))
diff --git a/lisp/programming/c++.el b/lisp/programming/c++.el
new file mode 100644
index 0000000..5dca93b
--- /dev/null
+++ b/lisp/programming/c++.el
@@ -0,0 +1,29 @@
+;;; c++.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the C++ programming language.
+;;;
+
+(use-package cc-mode
+ :straight nil
+ ;; Even though the Linux kernel coding style [1] was written to address C, I
+ ;; feel its viewpoints on indentation apply equally as well to C++.
+ ;;
+ ;; [1]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html
+ :hook
+ (c++-mode . (lambda ()
+ (setq indent-tabs-mode t)
+ (c-set-style "linux")))
+
+ ;; Enable company, Flycheck, and ElDoc, integration for all of which is
+ ;; provided by `irony-mode'.
+ :hook
+ (c++-mode . company-mode)
+ (c++-mode . flycheck-mode)
+ (c++-mode . eldoc-mode))
+
+(use-package irony
+ :hook
+ (c++-mode . irony-mode))
diff --git a/lisp/programming/c.el b/lisp/programming/c.el
new file mode 100644
index 0000000..6ba19e2
--- /dev/null
+++ b/lisp/programming/c.el
@@ -0,0 +1,42 @@
+;;; c.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the C programming language.
+;;;
+
+(use-package cc-mode
+ :straight nil
+
+ ;; Generally, I adhere to the Linux kernel coding style [1].
+ ;;
+ ;; [1]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html
+ :hook
+ (c-mode . (lambda ()
+ (setq indent-tabs-mode t)
+ (c-set-style "linux")))
+
+ ;; Enable company, Flycheck, and ElDoc, integration for all of which is
+ ;; provided by `irony-mode'.
+ :hook
+ (c-mode . company-mode)
+ (c-mode . flycheck-mode)
+ (c-mode . eldoc-mode))
+
+(use-package irony
+ :hook
+ (c-mode . irony-mode)
+ (irony-mode . irony-cdb-autosetup-compile-options))
+
+(use-package company-irony
+ :config
+ (add-to-list 'company-backend 'company-irony))
+
+(use-package flycheck-irony
+ :hook
+ (flycheck-mode . flycheck-irony-setup))
+
+(use-package irony-eldoc
+ :hook
+ (irony-mode . irony-eldoc))
diff --git a/lisp/programming/common-lisp.el b/lisp/programming/common-lisp.el
new file mode 100644
index 0000000..b3aa00b
--- /dev/null
+++ b/lisp/programming/common-lisp.el
@@ -0,0 +1,32 @@
+;;; common-lisp.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the Common Lisp programming
+;;; language.
+;;;
+
+;; SLIME is my preferred REPL for working with Common Lisp,
+
+(use-package slime
+ ;; and SBCL is my preferred implementation.
+ :config
+ (setq inferior-lisp-program "sbcl")
+
+ ;; Enable Smartparens and Company in the SLIME REPL.
+ :hook
+ (slime-repl-mode . (lambda ()
+ (smartparens-mode +1)
+ (company-mode +1)))
+
+ ;; Enable the SLIME backend for company.
+ :config
+ (setq slime-contribs '(slime-fancy slime-company)))
+
+(use-package slime-company
+ :after company
+ :hook
+ (lisp-mode . (lambda () (company-mode +1)))
+ :config
+ (add-to-list 'company-backends 'company-slime))
diff --git a/lisp/programming/elisp.el b/lisp/programming/elisp.el
new file mode 100644
index 0000000..0b975eb
--- /dev/null
+++ b/lisp/programming/elisp.el
@@ -0,0 +1,30 @@
+;;; elisp.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the Emacs Lisp programming
+;;; language.
+;;;
+
+(use-package elisp-mode
+ :straight nil
+ ;; Enable company for both `lisp-interaction-mode' and `emacs-lisp-mode'.
+ :hook
+ (lisp-interaction-mode . company-mode)
+ (emacs-lisp-mode . company-mode)
+
+ ;; Avoid omitting information when displaying deeply-nested lists.
+ :init
+ (setq eval-expression-print-level nil))
+
+;; Helpful is a wonderful improvement upon the vanilla `describe-.*' functions,
+;; capable of showing source code, keybindings, symbol properties, etc.
+
+(use-package helpful
+ :bind
+ ("C-h f" . helpful-callable)
+ ("C-h v" . helpful-variable)
+ ("C-h k" . helpful-key)
+ ("C-h C" . helpful-command)
+ ("C-c C-d" . helpful-at-point))
diff --git a/lisp/programming/javascript.el b/lisp/programming/javascript.el
new file mode 100644
index 0000000..c74e6ac
--- /dev/null
+++ b/lisp/programming/javascript.el
@@ -0,0 +1,26 @@
+;;; javascript.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the Javascript programming
+;;; language.
+;;;
+
+(use-package js
+ :straight nil
+
+ ;; Set up `js-mode' to interact with a LSP daemon.
+ :requires lsp-mode
+ :hook (js-mode . lsp)
+ :config
+ (lsp-register-client
+ (make-lsp-client :new-connection (lsp-stdio-connection "~/.bin/js-ts-stdio")
+ :major-modes '(js-mode)
+ :server-id 'js-ts-stdio))
+
+;; I have yet to find a style guide I agree with, but one thing common to most
+;; of them that I _do_ agree with is that Javascript is far more readable with
+;; an indentation width of 2.
+ :init
+ (setq-default js-indent-level 2))
diff --git a/lisp/programming/python.el b/lisp/programming/python.el
new file mode 100644
index 0000000..a6c7fc4
--- /dev/null
+++ b/lisp/programming/python.el
@@ -0,0 +1,24 @@
+;;; python.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the Python programming
+;;; language.
+;;;
+
+;; Set up company.
+
+(use-package company-jedi
+ :after company
+
+ :hook
+ (python-mode . company-mode)
+ (python-mode . jedi:setup)
+
+ :init
+ (setq jedi:complete-on-dot t)
+ (setq jedi:tooltip-method nil)
+
+ :config
+ (add-to-list 'company-backends 'company-jedi))
diff --git a/lisp/programming/rust.el b/lisp/programming/rust.el
new file mode 100644
index 0000000..6e4e43b
--- /dev/null
+++ b/lisp/programming/rust.el
@@ -0,0 +1,40 @@
+;;; rust.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the Rust programming
+;;; language.
+;;;
+
+;; Enable flycheck.
+
+(use-package flycheck-rust
+ :after rust-mode
+ :hook
+ (flycheck-mode . flycheck-rust-setup)
+ (rust-mode . flycheck-mode))
+
+;; Enable company, ElDoc, and racer.
+
+(use-package racer
+ :init
+ (setq racer-cmd "~/.cargo/bin/racer")
+ (setq racer-rust-src-path "~/Code/rust/src")
+
+ :hook
+ (racer-mode . eldoc-mode)
+ (racer-mode . company-mode))
+
+(use-package rust-mode
+ :hook
+ (rust-mode . racer-mode)
+
+ ;; Run 'rustfmt' automatically.
+ :init
+ (setq rust-format-on-save t))
+
+;; Enable keybindings for working with Cargo.
+
+(use-package cargo
+ :hook (rust-mode . cargo-minor-mode))
diff --git a/lisp/programming/scheme.el b/lisp/programming/scheme.el
new file mode 100644
index 0000000..de9f380
--- /dev/null
+++ b/lisp/programming/scheme.el
@@ -0,0 +1,21 @@
+;;; scheme.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration of a development environment for the Scheme programming
+;;; language.
+;;;
+
+;; Geiser is my preferred REPL for working with Scheme.
+
+(use-package geiser
+ ;; Set up Geiser for all of the Scheme implementations I regularly use.
+ :init
+ (setq geiser-active-implementations '(chicken guile racket))
+ (setq geiser-default-implementation 'guile)
+ (setenv "CHICKEN_DOC_REPOSITORY" "/home/jakob/.local/share/chicken/doc/")
+
+ ;; Filesystem hygene.
+ :init
+ (setq geiser-repl-history-filename "~/.cache/emacs/geiser_history"))
diff --git a/lisp/quality-of-life.el b/lisp/quality-of-life.el
new file mode 100644
index 0000000..b7fbe98
--- /dev/null
+++ b/lisp/quality-of-life.el
@@ -0,0 +1,86 @@
+;;; quality-of-life.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for packages which I consider to be functional improvements to
+;;; the quality of my daily Emacs usage.
+;;;
+
+
+;;;
+;;; Ivy
+;;;
+
+;; As suggested by a friend [1], I tried out `ivy' as a replacement for `ido.'
+;; It was love at first sight. It's snappy, and works even better than `ido'
+;; after setting `exwm-workspace-show-all-buffers' and
+;; `exwm-layout-show-all-buffers'.
+;;
+;; [1]: https://icosahedron.website/@zge/101330910258980695
+
+(use-package ivy
+ ;; Configure how the =completing-read= is displayed.
+ :init
+ (setq ivy-height 5 ivy-count-format "")
+
+ ;; Enable Ivy globally.
+ :config
+ (ivy-mode 1))
+
+;; `counsel' is a nice Ivy-based frontend for a couple of interactive commands.
+
+(use-package counsel
+ :demand t
+ :bind
+ ("C-x b" . counsel-switch-buffer))
+
+;; Use Swiper instead of isearch.
+
+;; TODO: Should I re-enable this?
+
+;; (use-package swiper
+;; :bind
+;; ("C-r" . swiper)
+;; ("C-s" . swiper))
+
+
+;;;
+;;; Imenu
+;;;
+
+(use-package imenu
+ :bind ("C-c s" . imenu))
+
+
+;;;
+;;; Yasnippet
+;;;
+
+;; Load all snippets when `yas-minor-mode' is enabled.
+
+(use-package yasnippet
+ :demand t
+ :hook
+ (yas-minor-mode . yas-reload-all))
+
+
+;;;
+;;; Avy
+;;;
+
+;; Set up keybindings for Avy.
+
+(use-package avy
+ :bind
+ ("M-s" . avy-goto-char))
+
+;; Install a link-selection package for Avy, and create a binding for `org-mode'.
+
+;; TODO: Should I re-enable this?
+
+;; (use-package ace-link
+;; :after org
+;; :init (ace-link-setup-default)
+;; :bind (:map org-mode-map
+;; ("C-c C-o" . ace-link-org)))
diff --git a/lisp/system.el b/lisp/system.el
new file mode 100644
index 0000000..3c317c6
--- /dev/null
+++ b/lisp/system.el
@@ -0,0 +1,68 @@
+;;; system.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for packages which work at the operating system level
+;;; (particularly the file system).
+;;;
+
+;; Set up keybindings for calling 'ag'.
+
+(use-package ag
+ :bind
+ ("C-c k" . counsel-ag))
+
+(use-package dired
+ :straight nil
+ ;; Hide the targets of symbolic links in `dired' listings. This is a visual
+ ;; preference of mine.
+ :init
+ (setq-default dired-listing-switches "-alL")
+
+ ;; Show '.eln' files the same way that '.elc' files are.
+ :config
+ (push ".eln" completion-ignored-extensions))
+
+(use-package dired-rmjunk
+ ;; Set up a keybinding for invoking `dired-rmjunk' from `dired-mode'.
+ :bind
+ (:map dired-mode-map
+ ("r" . dired-rmjunk))
+
+ ;; Implement my customizations from =rmshit.py=.
+ :init
+ (setq dired-rmjunk-patterns
+ '(".recently-used"
+ ".local/share/recently-used.xbel"
+ ".thumbnails"
+ ".gconfd"
+ ".gconf"
+ ".local/share/gegl-0.2"
+ ".gstreamer-0.10"
+ ".esd_auth"
+ ".config/enchant"
+ ".parallel"
+ ".dbus"
+ "ca2"
+ "ca2~"
+ ".nv/"
+ ".npm/"
+ ".java/"
+ ".oracle_jre_usage/"
+ ".jssc/"
+ ".tox/"
+ ".pylint.d/"
+ ".guile_history"
+ ".sqlite_history"
+ ".python_history"
+ ".hy-history"
+ ".lesshst"
+ ".audacity-data"
+ ".wget-hsts"
+ ".scapy_history"
+ ".gdb_history"
+ ".binwalk"
+ ".lyrics"
+ ".slime-history.eld"
+ ".w3m")))
diff --git a/lisp/tunes.el b/lisp/tunes.el
new file mode 100644
index 0000000..d85974a
--- /dev/null
+++ b/lisp/tunes.el
@@ -0,0 +1,20 @@
+;;; tunes.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; Configuration for using Emacs as an MPD [1] client.
+;;;
+;;; [1]: https://www.musicpd.org/
+;;;
+
+(use-package mpdel
+ :demand t
+ ;; Use a more sane prefix key for interacting with 'mpdel'.
+ :init
+ (setq mpdel-prefix-key (kbd "C-c z"))
+
+ ;; Start `mpdel-mode', and while we're at it, start the daemon, too.
+ :config
+ (shell-command "ps -e | grep -q mpd || mpd")
+ (mpdel-mode))
diff --git a/lisp/window-manager.el b/lisp/window-manager.el
new file mode 100644
index 0000000..fa332d4
--- /dev/null
+++ b/lisp/window-manager.el
@@ -0,0 +1,183 @@
+;;; window-manager.el -*- lexical-binding: t; -*-
+;;;
+;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; URL: https://git.sr.ht/~jakob/.emacs.d/
+;;;
+;;; My configuration for the EXWM package [1]. Yes, I use Emacs as my window
+;;; manager. You should, too!
+;;;
+;;; [1]: https://github.com/ch11ng/exwm
+;;;
+
+(use-package exwm
+ :config
+ (exwm-enable)
+
+ ;; I find that four workspaces is enough to start out with, and I like being
+ ;; able to use my X windows across workspaces.
+ :init
+ (setq exwm-workspace-number 4
+ exwm-workspace-show-all-buffers t
+ exwm-layout-show-all-buffers t)
+
+ ;; "Global key bindings" in EXWM work essentially anywhere, including buffers
+ ;; that are currently in char mode. I've taken this as an opportunity to
+ ;; incorporate windmove to my workflow, now that I have the super key at my
+ ;; disposal.
+ :init
+ (require 'windmove)
+
+ ;; Define some interactive functions for dealing with a multi-head setup.
+ (when (string= (system-name) "Epsilon")
+ (defun my-windmove-right ()
+ (interactive)
+ (if (windmove-find-other-window 'right)
+ (windmove-right)
+ (if (= exwm-workspace-current-index 1)
+ (exwm-workspace-switch 0)
+ (exwm-workspace-switch 1))))
+ (defun my-windmove-left ()
+ (interactive)
+ (if (windmove-find-other-window 'left)
+ (windmove-left)
+ (if (= exwm-workspace-current-index 1)
+ (exwm-workspace-switch 0)
+ (exwm-workspace-switch 1)))))
+
+ ;; And if we haven't defined those functions because we aren't on a multi-head
+ ;; setup, define aliases to the underlying windmove functions.
+ (when (and (not (fboundp #'my-windmove-left))
+ (not (fboundp #'my-windmove-right)))
+ (defalias #'my-windmove-left #'windmove-left)
+ (defalias #'my-windmove-right #'windmove-right))
+
+ (setq exwm-input-global-keys
+ `(([?\s-r] . exwm-reset)
+ ([?\s-a] . my-windmove-left)
+ ([?\s-d] . my-windmove-right)
+ ([?\s-w] . windmove-up)
+ ([?\s-s] . windmove-down)
+ ([?\s-p] . (lambda (command)
+ (interactive (list (read-shell-command "$ ")))
+ (start-process-shell-command command nil command)))
+ ([?\s-X] . (lambda ()
+ (interactive)
+ (start-process "" nil "slock")))
+ ([?\s-f] . (lambda ()
+ (interactive)
+ (start-process "" nil "firefox")))
+ (,(kbd "s-<return>") . (lambda ()
+ (interactive)
+ (start-process "" nil "alacritty")))
+
+ ;; Keys are offset such that <s-1> corresponds to the 0th workspace.
+ ,@(mapcar (lambda (i)
+ `(,(kbd (format "s-%d" i)) .
+ (lambda ()
+ (interactive)
+ (exwm-workspace-switch-create ,(- i 1)))))
+ (number-sequence 1 9))))
+
+ ;; Ah, yes. Simulation keys, the reason I decided to look at EXWM in the first
+ ;; place.
+ :init
+ (setq exwm-input-simulation-keys
+ '(([?\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])
+ ([?\M-v] . [prior])
+ ([?\C-v] . [next])
+ ([?\C-d] . [delete])
+ ([?\C-k] . [S-end delete])
+
+ ([?\C-w] . [?\C-x])
+ ([?\M-w] . [?\C-c])
+ ([?\C-y] . [?\C-v])
+
+ ([?\C-s] . [?\C-f])))
+
+ ;; The tab management functionality of Firefox's Saka Key extension is
+ ;; limited, so I'm using simulation keys instead. I've also disabled
+ ;; simulation keys for alacritty because it doesn't play nicely with readline.
+ :hook
+ (exwm-manage-finish . (lambda ()
+ (when (and exwm-class-name
+ (string= exwm-class-name "Firefox"))
+ (exwm-input-set-local-simulation-keys
+ (concatenate 'list
+ exwm-input-simulation-keys
+ '(([?\C-q] . [?\C-w])))))
+ (when (and exwm-class-name
+ (string= exwm-class-name "Alacritty"))
+ (exwm-input-set-local-simulation-keys nil))))
+
+ ;; EXWM's buffers are named '*EXWM*' upon creation, which makes switching
+ ;; between windows a pain. This snippet renames buffers when their class or
+ ;; title changes.
+ :hook
+ (exwm-update-class . (lambda ()
+ (exwm-workspace-rename-buffer exwm-class-name)))
+ (exwm-update-title . (lambda ()
+ (when (not exwm-instance-name)
+ (exwm-workspace-rename-buffer exwm-title))))
+
+
+ ;; Start the Emacs daemon so that 'emacsclient' can be used from the command
+ ;; line to pop a new buffer for editing.
+ :config
+ (server-start)
+
+ ;; Configure the resolution of my monitors and let EXWM know about them.
+ :config
+ (require 'exwm-randr)
+ (defun hdmi-out ()
+ (and (string= (system-name) "Upsilon")
+ (save-window-excursion
+ (with-temp-buffer
+ (shell-command "xrandr" (current-buffer))
+ (cl-search "HDMI-1 connected" (buffer-string))))))
+ (setq exwm-randr-workspace-monitor-plist
+ (if (string= (system-name) "Epsilon")
+ '(0 "DVI-D-0" 1 "HDMI-A-0")
+ '(0 (if (hdmi-out) "HDMI-1" "eDP-1"))))
+ (add-hook 'exwm-randr-screen-change-hook
+ (lambda ()
+ (start-process-shell-command
+ "xrandr" nil
+ (if (string= (system-name) "Epsilon")
+ "xrandr --output DVI-D-0 --primary --left-of HDMI-A-0"
+ (if (hdmi-out)
+ "xrandr --output HDMI-1 --mode 1920x1080 --output eDP-1 --off"
+ "xrandr --output eDP-1 --mode 1920x1080")))))
+ (exwm-randr-enable))
+
+;; Some keybindings for buffer-move that are analagous to my keybindings for
+;; windmove above.
+
+(use-package buffer-move
+ :bind
+ ("M-s-w" . buf-move-up)
+ ("M-s-a" . buf-move-left)
+ ("M-s-s" . buf-move-down)
+ ("M-s-d" . buf-move-right))
+
+;; EXWM has issues popping any sort of pinentry window, so I just let Emacs do
+;; pinentry's job.
+
+(use-package epa
+ :straight nil
+ :config
+ (setq epa-pinentry-mode 'loopback))
+
+;; And on that note, allow 'gpg-agent' to query Emacs for my passphrase.
+
+(use-package pinentry
+ :demand t
+ :config
+ (setenv "INSIDE_EMACS" (format "%s,comint" emacs-version))
+ (pinentry-start))

© 2015 - 2026 Jakob L. Kreuze