summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/td-common.el145
-rw-r--r--lisp/td-dired.el27
-rw-r--r--lisp/td-evil.el33
-rw-r--r--lisp/td-functions.el64
-rw-r--r--lisp/td-mu4e.el63
-rw-r--r--lisp/td-org.el172
-rw-r--r--lisp/td-present.el27
-rw-r--r--lisp/td-programming.el123
-rw-r--r--lisp/td-writing.el22
9 files changed, 676 insertions, 0 deletions
diff --git a/lisp/td-common.el b/lisp/td-common.el
new file mode 100644
index 0000000..7e71f1b
--- /dev/null
+++ b/lisp/td-common.el
@@ -0,0 +1,145 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package async
+ :ensure t
+ :config
+ (async-bytecomp-package-mode 1))
+
+(use-package beframe
+ :ensure t
+ :config
+ (global-set-key (kbd "C-c b") 'beframe-prefix-map)
+ (beframe-mode 1))
+
+(use-package consult
+ :ensure t
+ :demand t
+ :bind (("C-s" . consult-ripgrep)
+ ("C-M-l" . consult-imenu)
+ ("C-M-j" . consult-buffer)
+ ("C-x C-b" . consult-buffer)
+ :map minibuffer-local-map
+ ("C-r" . consult-history)))
+
+(use-package consult-dir
+ :ensure t
+ :bind (("C-x C-d" . consult-dir)
+ :map vertico-map
+ ("C-x C-d" . consult-dir)
+ ("C-x C-j" . consult-dir-jump-file))
+ :custom
+ (consult-dir-project-list-function nil))
+
+(use-package doom-modeline
+ :ensure t
+ :init
+ (doom-modeline-mode 1)
+ :custom
+ (doom-modeline-height 10)
+ (doom-modeline-buffer-encoding nil)
+ (doom-modeline-modal-icon nil))
+
+(use-package ef-themes
+ :ensure t
+ :config
+ ;; By default start with a light theme.
+ (load-theme 'ef-day t))
+
+(use-package embark
+ :ensure t
+ :bind (("C-." . embark-act)
+ ("M-." . embark-dwim)
+ ("C-h B" . embark-bindings))
+ :config
+ ;; Remove mixed indicator to prevent popup from being displayed automatically.
+ (delete #'embark-mixed-indicator embark-indicators)
+ (add-to-list 'embark-indicators 'embark-minimal-indicator)
+
+ ;; Use embark to show command prefix help.
+ (setq prefix-help-command #'embark-prefix-help-command))
+
+(use-package embark-consult
+ :ensure t
+ :after embark)
+
+(use-package helpful
+ :ensure t
+ :custom
+ (counsel-describe-function-function #'helpful-callable)
+ (counsel-describe-variable-function #'helpful-variable)
+ :bind
+ ([remap describe-function] . helpful-function)
+ ([remap describe-command] . helpful-command)
+ ([remap describe-symbol] . helpful-symbol)
+ ([remap describe-variable] . helpful-variable)
+ ([remap describe-key] . helpful-key))
+
+(use-package marginalia
+ :ensure t
+ :after vertico
+ :custom
+ (marginalia-annotators '(marginalia-annotators-heavy
+ marginalia-annotators-light
+ nil))
+ :config
+ (marginalia-mode))
+
+(use-package no-littering
+ :ensure t
+ :demand t
+ :config
+ ;; Set custom-file to a file that won't be tracked by git.
+ (setq custom-file
+ (let ((custom-file "custom.el"))
+ (if (boundp 'server-socket-dir)
+ (expand-file-name custom-file server-socket-dir)
+ (no-littering-expand-etc-file-name custom-file))))
+ (when (file-exists-p custom-file)
+ (load custom-file t))
+
+ ;; Don't litter project folders with backup files.
+ (let ((backup-dir (no-littering-expand-var-file-name "backup/")))
+ (make-directory backup-dir t)
+ (setq backup-directory-alist
+ `(("\\`/tmp/" . nil)
+ ("\\`/dev/shm/" . nil)
+ ("." . ,backup-dir))))
+
+ ;; Tidy up auto-save files.
+ (setq auto-save-default nil)
+ (let ((auto-save-dir (no-littering-expand-var-file-name "auto-save/")))
+ (make-directory auto-save-dir t)
+ (setq auto-save-file-name-transforms
+ `(("\\`/[^/]*:\\([^/]*/\\)*\\([^/]*\\)\\'" ,(concat temporary-file-directory "\\2") t)
+ ("\\`\\(/tmp\\|/dev/shm\\)\\([^/]*/\\)*\\(.*\\)\\'" "\\3")
+ ("." ,auto-save-dir t)))))
+
+(use-package savehist
+ :ensure t
+ :init
+ (savehist-mode))
+
+(use-package vertico
+ :ensure t
+ :demand t
+ :bind (:map vertico-map
+ ("C-j" . vertico-next)
+ ("C-k" . vertico-previous)
+ ("C-f" . vertico-exit-input)
+ :map minibuffer-local-map
+ ("M-h" . vertico-directory-up))
+ :custom
+ (vertico-cycle t)
+ :config
+ (require 'vertico-directory)
+ (vertico-mode))
+
+(use-package which-key
+ :ensure t
+ :defer 0
+ :diminish which-key-mode
+ :config
+ (setq which-key-idle-delay 0.5)
+ (which-key-mode))
+
+(provide 'td-common)
diff --git a/lisp/td-dired.el b/lisp/td-dired.el
new file mode 100644
index 0000000..d1a6b72
--- /dev/null
+++ b/lisp/td-dired.el
@@ -0,0 +1,27 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package dired
+ :ensure nil
+ :commands (dired dired-jump)
+ :hook ((dired-mode . dired-hide-details-mode))
+ :bind (:map dired-mode-map ("C-x C-j" . dired-jump))
+ :config
+ (setq dired-listing-switches "-Agho --group-directories-first"
+ dired-omit-files "^\\.[^.].*"
+ dired-omit-verbose nil
+ dired-hide-detailes-hide-symlink-targets nil
+ dired-kill-when-opening-new-dired-buffer t
+ delete-by-moving-to-trash t))
+
+(use-package all-the-icons-dired
+ :ensure t
+ :hook (dired-mode . all-the-icons-dired-mode))
+
+(use-package dired-hide-dotfiles
+ :ensure t
+ :hook (dired-mode . dired-hide-dotfiles-mode)
+ :config
+ (evil-collection-define-key 'normal 'dired-mode-map
+ "H" 'dired-hide-dotfiles-mode))
+
+(provide 'td-dired)
diff --git a/lisp/td-evil.el b/lisp/td-evil.el
new file mode 100644
index 0000000..afa92a7
--- /dev/null
+++ b/lisp/td-evil.el
@@ -0,0 +1,33 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package evil
+ :init
+ (setq evil-want-integration t)
+ (setq evil-want-keybinding nil)
+ (setq evil-want-C-u-scroll t)
+ (setq evil-want-C-i-jump nil)
+ (setq evil-insert-state-cursor t)
+ :config
+ (evil-mode 1)
+
+ ;; Fix keybindings.
+ (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
+ (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)
+
+ ;; Use visual line motions even outside of visual-line-mode buffers.
+ (evil-global-set-key 'motion "j" 'evil-next-visual-line)
+ (evil-global-set-key 'motion "k" 'evil-previous-visual-line)
+
+ ;; Ensure some buffers start in normal mode.
+ (evil-set-initial-state 'messages-buffer-mode 'normal)
+ (evil-set-initial-state 'dashboard-mode 'normal))
+
+(use-package evil-collection
+ :after evil
+ :config
+ (evil-collection-init))
+
+(use-package evil-nerd-commenter
+ :bind ("M-/" . evilnc-comment-or-uncomment-lines))
+
+(provide 'td-evil)
diff --git a/lisp/td-functions.el b/lisp/td-functions.el
new file mode 100644
index 0000000..4475037
--- /dev/null
+++ b/lisp/td-functions.el
@@ -0,0 +1,64 @@
+;;; -*- lexical-binding: t; -*-
+
+(defun td/display-startup-time ()
+ (message (if (daemonp)
+ "emacs daemon loaded in %s with %d garbage collections."
+ "emacs loaded in %s with %d garbage collections.")
+ (format "%.2f seconds"
+ (float-time
+ (time-subtract after-init-time before-init-time)))
+ gcs-done))
+
+(defun td/set-font ()
+ (when (display-graphic-p)
+ (message "Setting font...")
+ (set-face-attribute 'default nil
+ :font "Iosevka Comfy Motion Fixed"
+ :weight 'normal
+ :height 200)
+ (set-face-attribute 'fixed-pitch nil
+ :font "Iosevka Comfy Motion Fixed"
+ :weight 'normal
+ :height 200)
+ (set-face-attribute 'variable-pitch nil
+ :font "Iosevka Comfy Motion Fixed"
+ :weight 'normal
+ :height 200)))
+
+(defun td/increment-number-at-point (&optional increment)
+ "Increment number at point like vim's `C-a'."
+ (interactive "p")
+ (td/change-number-at-point '+ (or increment 2)))
+
+(defun td/decrement-number-at-point (&optional decrement)
+ "Decrement number at point like vim's `C-x'."
+ (interactive "p")
+ (td/change-number-at-point '- (or decrement 2)))
+
+(defun td/change-number-at-point (change operation)
+ "The generic logic for incrementing and decrementing numbers at point."
+ (search-forward-regexp (rx digit))
+ (let ((number (number-at-point))
+ (point (point)))
+ (when number
+ (forward-word)
+ (search-backward (number-to-string number))
+ (replace-match (number-to-string (funcall change number operation)))
+ (goto-char (- point 1)))))
+
+(defun td/disable-theme ()
+ "Disable all themes."
+ (interactive)
+ (dolist (theme custom-enabled-themes)
+ (disable-theme theme)))
+
+(defun td/toggle-theme ()
+ "Toggle between light and dark modes."
+ (interactive)
+ (let ((theme (if (eq (car custom-enabled-themes) 'ef-dark)
+ 'ef-day
+ 'ef-dark)))
+ (td/disable-theme)
+ (load-theme theme t)))
+
+(provide 'td-functions)
diff --git a/lisp/td-mu4e.el b/lisp/td-mu4e.el
new file mode 100644
index 0000000..6e45fbb
--- /dev/null
+++ b/lisp/td-mu4e.el
@@ -0,0 +1,63 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package mu4e
+ :ensure nil
+ :bind (("C-c m m" . mu4e)
+ ("C-c m c" . 'mu4e-compose-new)
+ ("C-c m u" . 'mu4e-update-mail-and-index))
+ :config
+ (setq mu4e-maildir "~/Mail")
+
+ ;; IMAP options.
+ (setq mu4e-update-interval (* 5 60))
+ (setq mu4e-get-mail-command "mbsync -a")
+ (setq mu4e-change-filenames-when-moving t)
+
+ ;; SMTP options.
+ (setq message-send-mail-function 'message-send-mail-with-sendmail)
+ (setq sendmail-program "/etc/profiles/per-user/tdback/bin/msmtp")
+
+ ;; Compose options.
+ (setq mu4e-compose-format-flowed t)
+ (setq mu4e-compose-dont-reply-to-self t)
+ (setq message-kill-buffer-on-exit t)
+
+ ;; Display options.
+ (setq mu4e-view-show-images t)
+ (setq mu4e-view-show-addresses t)
+ (setq mu4e-modeline-show-global nil)
+
+ (setq mu4e-contexts
+ (list
+ ;; Personal account.
+ (make-mu4e-context
+ :name "Personal"
+ :vars '((user-mail-address . "tyler@tdback.net")
+ (user-full-name . "Tyler Dunneback")
+ (mu4e-compose-signature . "Tyler Dunneback")
+ (mu4e-drafts-folder . "/tdback/Drafts")
+ (mu4e-sent-folder . "/tdback/Sent")
+ (mu4e-refile-folder . "/tdback/Archive")
+ (mu4e-trash-folder . "/tdback/Trash")))))
+
+ (setq mu4e-maildir-shortcuts
+ '(("/tdback/Inbox" . ?i)
+ ("/tdback/Sent" . ?s)
+ ("/tdback/Trash" . ?t)
+ ("/tdback/Drafts" . ?d)
+ ("/tdback/Archive" . ?a)
+ ("/tdback/All Mail" . ?m))))
+
+(use-package org-mime
+ :ensure t
+ :config
+ (setq org-mime-export-options (list :section-numbers nil
+ :with-author nil
+ :with-toc nil))
+ (add-hook 'org-mime-html-hook (lambda ()
+ (org-mime-change-element-style
+ "pre" (format "color: %s; background-color: %s; padding: 0.5em;"
+ "#E6E1DC" "#232323"))))
+ (add-hook 'message-send-hook 'org-mime-confirm-when-no-multipart))
+
+(provide 'td-mu4e)
diff --git a/lisp/td-org.el b/lisp/td-org.el
new file mode 100644
index 0000000..d9ca50e
--- /dev/null
+++ b/lisp/td-org.el
@@ -0,0 +1,172 @@
+;;; -*- lexical-binding: t; -*-
+
+;;; ----- Bootstrapping Functions -----
+
+(defun td/org-font-setup ()
+ (font-lock-add-keywords 'org-mode
+ '(("^ *\\([-]\\) "
+ (0 (prog1 ()
+ (compose-region (match-beginning 1)
+ (match-end 1)
+ "•"))))))
+ (dolist (face '((org-level-1 . 1.2)
+ (org-level-2 . 1.15)
+ (org-level-3 . 1.1)
+ (org-level-4 . 1.0)))
+ (set-face-attribute (car face)
+ nil
+ :font "Iosevka Comfy Motion Fixed"
+ :weight 'regular
+ :height (cdr face)))
+ (set-face-attribute 'org-block
+ nil
+ :foreground nil
+ :inherit 'fixed-pitch)
+ (set-face-attribute 'org-code
+ nil
+ :foreground nil
+ :inherit '(shadow fixed-pitch))
+ (set-face-attribute 'org-table
+ nil
+ :foreground nil
+ :inherit '(shadow fixed-pitch))
+ (set-face-attribute 'org-verbatim
+ nil
+ :foreground nil
+ :inherit '(shadow fixed-pitch))
+ (set-face-attribute 'org-special-keyword
+ nil
+ :foreground nil
+ :inherit '(font-lock-comment-face fixed-pitch))
+ (set-face-attribute 'org-meta-line
+ nil
+ :foreground nil
+ :inherit '(font-lock-comment-face fixed-pitch))
+ (set-face-attribute 'org-checkbox
+ nil
+ :inherit 'fixed-pitch))
+
+(defun td/org-mode-setup ()
+ (org-indent-mode)
+ (variable-pitch-mode 1)
+ (visual-line-mode 1)
+ (setq evil-auto-indent nil))
+
+;;; ----- Package Declaration -----
+
+(use-package org
+ :ensure t
+ :hook (org-mode . td/org-mode-setup)
+ :bind (("C-c c" . org-capture)
+ ("C-c a" . org-agenda))
+ :config
+ (setq org-ellipsis " ▾"
+ org-hide-emphasis-markers t
+ org-agenda-start-with-log-mode t
+ org-log-done 'time
+ org-fontify-whole-heading-line t
+ org-fontify-quote-and-verse-blocks t
+ org-src-tab-acts-natively t
+ org-edit-src-content-indentation 2
+ org-hide-block-startup nil
+ org-log-into-drawer t
+ org-agenda-files '("~/Documents/org/tasks.org"
+ "~/Documents/org/ideas.org")
+ org-agenda-custom-commands '(("d" "Dashboard"
+ ((agenda ""
+ ((org-deadline-warning-days 7)))
+ (todo "NEXT"
+ ((org-agenda-overriding-header "Next Tasks")))))
+ ("n" "Next Tasks"
+ ((todo "NEXT"
+ ((org-agenda-overriding-header "Next Tasks")))))
+ ("s" "Status"
+ (todo "ACTIVE"
+ ((org-agenda-overriding-header "In Progress")
+ (org-agenda-files org-agenda-files)))
+ ((todo "BACKLOG"
+ ((org-agenda-overriding-header "Backlog")
+ (org-agenda-todo-list-sublevels nil)
+ (org-agenda-files org-agenda-files)))
+ (todo "CANCELED"
+ ((org-agenda-overriding-header "Canceled")
+ (org-agenda-files org-agenda-files)))
+ (todo "COMPLETED"
+ ((org-agenda-overriding-header "Completed")
+ (org-agenda-files org-agenda-files)))
+ ((todo "PLAN"
+ ((org-agenda-overriding-header "In Planning")
+ (org-agenda-todo-list-sublevels nil)
+ (org-agenda-files org-agenda-files)))
+ (todo "READY"
+ ((org-agenda-overriding-header "Ready for Work")
+ (org-agenda-files org-agenda-files)))))))
+ org-capture-templates '(("t" "Tasks")
+ ("tt" "New Task" entry (file+olp (car org-agenda-files))
+ "* TODO %?\n %i" :empty-lines 1)
+ ("i" "Ideas")
+ ("ii" "New Idea" entry (file+olp (cadr org-agenda-files))
+ "* %^{Idea}\n %U\n %?\n %i" :empty-lines 1))
+ org-todo-keywords '((sequence "TODO(t)"
+ "NEXT(n)"
+ "|"
+ "DONE(d!)")
+ (sequence "BACKLOG(b)"
+ "PLAN(p)"
+ "READY(r)"
+ "ACTIVE(a)"
+ "|"
+ "COMPLETED(c)"
+ "CANCELED(k@)"))
+ org-refile-targets '(("archive.org" :maxlevel . 1)
+ ("tasks.org" :maxlevel . 1)))
+ (advice-add 'org-refile :after 'org-save-all-org-buffers)
+ (td/org-font-setup))
+
+(use-package org-appear
+ :ensure t
+ :after org
+ :custom
+ (org-hide-emphasis-markers t)
+ (org-appear-autolinks t)
+ (org-appear-inside-latex t)
+ (org-appear-autoentities t)
+ (org-appear-autosubmarkers t)
+ :config
+ (add-hook 'org-mode-hook 'org-appear-mode))
+
+(use-package org-modern
+ :ensure t
+ :hook ((org-mode . org-modern-mode)
+ (org-agenda-finalize-hook . org-modern-agenda))
+ :custom ((org-modern-todo t)
+ (org-modern-table nil)
+ (org-modern-variable-pitch nil)
+ (org-modern-block-fringe nil))
+ :commands
+ (org-modern-mode org-modern-agenda)
+ :init
+ (global-org-modern-mode))
+
+(use-package org-timeline
+ :ensure t
+ :commands org-agenda
+ :init
+ (add-hook 'org-agenda-finalize-hook 'org-timeline-insert-timeline :append))
+
+;; Code block execution and template expansion.
+(with-eval-after-load 'org
+ (org-babel-do-load-languages 'org-babel-load-languages
+ '((emacs-lisp . t)
+ (python . t)))
+
+ (push '("conf-unix" . conf-unix) org-src-lang-modes)
+
+ (require 'org-tempo)
+ (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
+ (add-to-list 'org-structure-template-alist '("py" . "src python"))
+ (add-to-list 'org-structure-template-alist '("rs" . "src rust"))
+ (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
+ (add-to-list 'org-structure-template-alist '("sq" . "src sqlite")))
+
+(provide 'td-org)
diff --git a/lisp/td-present.el b/lisp/td-present.el
new file mode 100644
index 0000000..72cd901
--- /dev/null
+++ b/lisp/td-present.el
@@ -0,0 +1,27 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package org-present
+ :ensure t
+ :hook ((org-present-mode . td/org-present-start)
+ (org-present-mode-quit . td/org-present-end)
+ (org-present-after-navigate-functions . td/org-present-prepare-slide)))
+
+(defun td/org-present-start ()
+ (setq-local face-remapping-alist
+ '((default (:height 1.5) variable-pitch)
+ (header-line (:height 4.0) variable-pitch)
+ (org-document-title (:height 1.75) org-document-title)
+ (org-code (:height 1.55) org-code)
+ (org-verbatim (:height 1.55) org-verbatim)
+ (org-block (:height 1.25) org-block)
+ (org-block-begin-line (:height 0.7) org-block))))
+
+(defun td/org-present-end ()
+ (setq-local face-remapping-alist '((default variable-pitch default))))
+
+(defun td/org-present-prepare-slide (buffer-name heading)
+ (org-overview)
+ (org-show-entry)
+ (org-show-children))
+
+(provide 'td-present)
diff --git a/lisp/td-programming.el b/lisp/td-programming.el
new file mode 100644
index 0000000..eee8d67
--- /dev/null
+++ b/lisp/td-programming.el
@@ -0,0 +1,123 @@
+;;; -*- lexical-binding: t; -*-
+
+;;; ----- Completion -----
+
+(use-package cape
+ :defer 10
+ :init
+ ;; Add `completion-at-point-functions', used by `completion-at-point'.
+ (add-to-list 'completion-at-point-functions #'cape-file)
+ ;; Nice completion to have available everywhere.
+ (add-to-list 'completion-at-point-functions #'cape-dabbrev)
+ :config
+ ;; Silence then pcomplete capf, no errors or messages.
+ (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)
+ ;; Ensure that pcomplete does not write to the buffer and behaves as a pure
+ ;; `completion-at-point-function'.
+ (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify))
+
+(use-package corfu
+ :ensure t
+ :custom
+ (corfu-cycle t) ; Cycle through candidates
+ (corfu-auto t) ; Enable auto completion.
+ (corfu-auto-prefix 2) ; Display completion options after two characters.
+ (corfu-auto-delay 0.0) ; Don't delay.
+ (corfu-quit-at-boundary 'separator) ; Quit if no separator has been inserted at the boundary.
+ (corfu-echo-documentation 0.25) ; Echo docs shortly after options.
+ (corfu-preview-current 'insert) ; Auto-insert the current completion.
+ (corfu-preselect-first nil) ; Don't select a completion right away.
+ :bind (:map corfu-map
+ ("M-SPC" . corfu-insert-separator)
+ ("C-n" . corfu-next)
+ ([tab] . corfu-next)
+ ("C-p" . corfu-previous)
+ ([backtab] . corfu-previous)
+ ("S-<return>" . corfu-insert)
+ ("RET" . nil))
+ :init
+ ;; Use corfu everywhere.
+ (global-corfu-mode)
+ ;; Save completion history for better sorting.
+ (corfu-history-mode))
+
+(use-package eglot
+ :ensure t
+ :defer t
+ :config
+ (setq eglot-autoshutdown t
+ elgot-ignored-server-capabilities '(:inlayHintProvider))
+ (add-to-list 'eglot-server-programs '(c-mode . ("clangd")))
+ (add-to-list 'eglot-server-programs '(c++-mode . ("clangd")))
+ (add-to-list 'eglot-server-programs '(nix-mode . ("nixd")))
+ (add-to-list 'eglot-server-programs '(python-mode . ("pylsp")))
+ (add-to-list 'eglot-server-programs '(rust-mode . ("rust-analyzer")))
+ :hook
+ ((c-mode . eglot-ensure)
+ (c++-mode . eglot-ensure)
+ (nix-mode . eglot-ensure)
+ (python-mode . eglot-ensure)
+ (rust-mode . eglot-ensure)))
+
+(use-package orderless
+ :ensure t
+ :commands (orderless)
+ :custom
+ (completion-styles '(orderless flex)))
+
+;;; ----- Modes -----
+
+(use-package dockerfile-mode
+ :ensure t
+ :defer t)
+
+(use-package markdown-mode
+ :ensure t
+ :defer t)
+
+(use-package nix-mode
+ :ensure t
+ :defer t)
+
+(use-package python-mode
+ :ensure t
+ :defer t)
+
+(use-package rust-mode
+ :ensure t
+ :defer t
+ :config
+ (setq rust-format-on-save nil))
+
+(use-package toml-mode
+ :ensure t
+ :defer t)
+
+(use-package yaml-mode
+ :ensure t
+ :defer t)
+
+;;; ----- Tooling & Enhancements -----
+
+(use-package compile
+ ;; Using `C-u' before recompile acts like `M-x compile'.
+ :bind (("C-x C-m" . recompile))
+ :custom
+ (compilation-scroll-output t))
+
+(use-package magit
+ :ensure t
+ :commands magit-status)
+
+(use-package paren-face
+ :ensure t
+ :hook ((prog-mode
+ eshell-mode
+ inferior-lisp-mode
+ inferior-emacs-lisp-mode
+ lisp-interaction-mode
+ sly-mrepl-mode
+ scheme-mode)
+ . paren-face-mode))
+
+(provide 'td-programming)
diff --git a/lisp/td-writing.el b/lisp/td-writing.el
new file mode 100644
index 0000000..df9989f
--- /dev/null
+++ b/lisp/td-writing.el
@@ -0,0 +1,22 @@
+;;; -*- lexical-binding: t; -*-
+
+(use-package jinx
+ :ensure t
+ :hook ((org-mode . jinx-mode)
+ (text-mode . jinx-mode))
+ :bind (("M-$" . jinx-correct)
+ ("C-M-$" . jinx-languages)))
+
+(use-package olivetti
+ :ensure t
+ :defer t
+ :hook ((markdown-mode . olivetti-mode)
+ (org-mode . olivetti-mode))
+ :init
+ (setq olivetti-body-width 80))
+
+(use-package writegood-mode
+ :ensure t
+ :hook (jinx-mode . writegood-mode))
+
+(provide 'td-writing)