Emacs Init File!
This is my emacs configuration. I keep it in org-mode and automatically tangle
it to ~/.config/emacs/init.el on save (see Local vars). This makes it easy to
document and organize.
Most of the functions and variables I define myself are prefixed with d/, just
so I can keep track of them.
If you have a question about any of the built-in variables, functions, modes,
etc. that you see here, M-x describe-symbol (bound to C-h o in vanilla Emacs)
might be a good place to start.
Initial Setup
Settings
Lexical binding is a variable scope matter in Emacs lisp. I won't explain it here but I've linked the info page for it. Anyway, it's a good idea to turn this on by default.
This may be the default in a future version of Emacs.
;;; -*- lexical-binding: t -*-
I like to keep a local "lisp" directory in my emacs config for slightly more involved lisp projects that I don't want cluttering my init and may not want to officially package.
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
Some libraries I find useful within my configuration.
(require 'cl-lib) (require 'subr-x)
Utils
This is just a convenience macro around with-eval-after-load. Often I want do things after a package loads (and not in the same form as use-package, mostly for organizational reasons). I don't want errors to totally stop my startup.
(defmacro d/after (feature &rest body) "Load BODY after FEATURE, catching errors and displaying as warnings." (declare (indent defun)) `(with-eval-after-load ,feature (condition-case-unless-debug err (progn ,@body) (error (display-warning 'init (format "%s eval-after-load: %s " (symbol-name ,feature) (error-message-string err)) :error)))))
Package management
elpaca
Elpaca is a source-based, parallel package manager. It's a kind of successor to straight.el. It's useful if you do a lot of hacking on elisp packages, or need to pin specific versions of packages. The parallelism is nice, too.
At some point, I may switch back to the built-in package.el with the :vc
keyword to use-package in special cases. Or maybe not, we'll see.
(defvar elpaca-installer-version 0.12) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-sources-directory (expand-file-name "sources/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca-activate))) (let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (<= emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (let ((load-source-file-function nil)) (load "./elpaca-autoloads")))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) ;; Install use-package support (elpaca elpaca-use-package ;; Enable :ensure use-package keyword. (elpaca-use-package-mode)) ;; Block until current queue processed. (elpaca-wait)
use-package
An excellent utility for managing packages and package configuration in a neat and organized way, with advanced support for deferring, pre/post-loading configuration, time reporting, and more.
You can use the same init file across computers without keeping track of what's installed or not and it will ensure that any missing packages are installed. You can also conditionalize package load by system (or anything else). It's pretty neat.
Read the manual linked above!
(setq use-package-minimum-reported-time .001 use-package-verbose t use-package-always-defer t use-package-always-ensure t use-package-compute-statistics t)
Packages
compat
Compat is a forwards-compatibility library. I have this here because the vertico stack recently broke on me and this fixed it. Honestly, I'm not exactly sure what that was about.
(use-package compat :ensure (:wait t) :demand t :config (require 'compat-31)) (elpaca-wait)
no-littering
Usually, a bunch of files are kept in your .emacs.d folder by both built-in
emacs features and external packages. This package sets up a convention to
store everything in either .emacs.d/var for data, or .emacs.d/etc for
configuration.
(use-package no-littering :demand t :config (no-littering-theme-backups) (setq custom-file (no-littering-expand-etc-file-name "custom.el")) (add-hook 'elpaca-after-init-hook (lambda () (load custom-file 'noerror))))
hydra
Hydra lets you create small, transient keymaps with a persistent help menu. They tend to quit when you pressa key not in the hydra. I don't use this as much as I used to, but I still have some hydras defined in my init.
(use-package hydra :config (setq hydra-default-hint nil))
savehist
savehist is a built-in mode to save minibuffer (and other) histories between emacs sessions.
(use-package savehist :ensure nil :defer 5 :config (savehist-mode 1) (add-to-list 'savehist-additional-variables 'kill-ring))
saveplace
saveplace is a built-in mode to save and re-visit your last position in files after you close them (or emacs).
(use-package saveplace :ensure nil :defer 5 :config (save-place-mode 1))
midnight
midnight is a bulit-in mode to clean up saved buffers that haven't been displayed recently on some regular interval, by default a day.
(use-package midnight :ensure nil :defer 10 :custom (clean-buffer-list-delay-general 1) (clean-buffer-list-kill-regexps '("\\`\\*Man " "\\`\\*helpful ")) :config (midnight-mode))
Core
Settings
Don't show me the startup screen, just get me to the editor!
(setq inhibit-startup-screen t inhibit-startup-echo-area-message t)
Start the scratch buffer in fundamental-mode with no message. This is to avoid
emacs-lisp-mode and prog-mode hooks from slowing down startup. We get back our
old scratch buffer eventually with persistent-scratch anyway.
(setq initial-scratch-message "" initial-major-mode 'fundamental-mode)
Use TAB for indentation, then completion.
(setq tab-always-indent 'complete)
Stop beeping!
(setq ring-bell-function #'ignore)
Copy pasting settings:
- Save current external clipboard item to kill ring
- Enable the primary selection; this is… not so relevant to me these days.
(setq save-interprogram-paste-before-kill t select-enable-primary t)
Minibuffer settings:
- Use the minibuffer… from the minibuffer
- Resize minibuffer windows to fit content
- Make the minibuffer prompt untouchable
- Indicate minibuffer depth lest we lose our minds
(setq enable-recursive-minibuffers t resize-mini-windows t minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (minibuffer-depth-indicate-mode t)
More messages!
(setf message-log-max 10000)
A couple other things:
- Don't put native comp warnings front and center; it can be noisy.
- Don't disable any commands; I generally know what I'm doing.
- When killing emacs, just kill it without asking to kill processes.
- Disable
view-hello-file, because I keep accidentally pressingC-h hsomehow. This is silly.
(setf native-comp-async-report-warnings-errors 'silent disabled-command-function nil confirm-kill-processes nil) (defalias #'view-hello-file #'ignore)
Packages
These are packages that I consider absolutely essential to my emacs workflow, or that enhance emacs at a deeper level than any regular mode.
darktooth-theme
…because MY EYES! Some people argue dark themes are worse for your vission or less legible. This may be true. Oh well.
This is my own fork of the most excellent emacsfodder/emacs-theme-darktooth with many of my own tweaks.
(use-package darktooth-theme :demand t :ensure (:host github :repo "emacsfodder/emacs-theme-darktooth" :remotes ("fork" :host github :repo "dieggsy/emacs-theme-darktooth")) :custom (custom-safe-themes t) :config (load-theme 'darktooth))
general
general is a convenient, stable framework for defining your own key-bindings,
especially in evil-mode. I use this to define "leader key" bindings under SPC
in the style of Vim's <leader>, as well as a mode-specific leader bindings
under ,.
(use-package general :demand t :config (general-evil-setup t) (general-override-mode) (general-create-definer d/mode-leader-keys :keymaps 'override :states '(emacs normal visual motion insert) :non-normal-prefix "C-," :prefix ",") (general-create-definer d/leader-keys :keymaps 'override :states '(emacs normal visual motion insert) :non-normal-prefix "C-SPC" :prefix "SPC")) (elpaca-wait)
evil stack
evil is an emacs package that emulates Vim key bindings. It's solid and quite extensible, and there are several packages that enhance the experience or make it more consistent across Emacs.
There may come a day when the ubiquity of Vim fails, when we forsake evil-mode, and break all bonds of verb-noun editing… but it is not this day!
I've considered switching to meow. Part of the problem is that vi/vim is everywhere as a program or as an emulation layer, which makes vim bindings quite a comfy default. Still, meow calls to me…
evil
This is the core evil package. I have some settings here to make it more vimmy, some that make it less I also set some cursor types for indicating what evil state (like vim mode) I'm in.
(use-package evil :demand t :general (mmap "-" 'negative-argument ;; Basically C-[ for a Dvorak keyboard (_ is for terminal). "C-_" 'keyboard-quit "C-/" 'keyboard-quit [escape] 'keyboard-quit) (:states '(insert replace visual) "C-_" 'evil-normal-state "C-/" 'evil-normal-state) (vmap [escape] 'keyboard-quit) :init (setq evil-search-module 'evil-search) :custom (evil-want-C-u-delete t) (evil-want-C-u-scroll t) (evil-want-keybinding nil) (evil-want-fine-undo t) (evil-undo-system 'undo-fu) (evil-symbol-word-search t) (evil-ex-search-vim-style-regexp t) (evil-respect-visual-line-mode t) (evil-split-window-below t) (evil-vsplit-window-right t) :config (setq evil-insert-state-cursor '(bar . 1) ;thinner cursors evil-emacs-state-cursor '(hbar . 1) ;; Set these cursor types for evil-terminal-cursor-changer evil-normal-state-cursor 'box evil-motion-state-cursor 'box evil-visual-state-cursor 'box))
This is a small evil-ex alias for delete-trailing-whitespace, which I use quite
a bit, unless whitespace-cleanup-mode is doing the job for me.
(d/after 'evil (evil-ex-define-cmd "dtw" #'delete-trailing-whitespace) (evil-ex-define-cmd "bk" #'kill-current-buffer))
Ok, let there be Vim!
(d/after 'evil (evil-mode 1))
evil-nerd-commenter
This package provides emulation for Vim's Nerd Commenter; it it provides useful utilities for commenting things out.
(use-package evil-nerd-commenter :general (nmap "gc" 'evilnc-comment-operator "gy" 'evilnc-copy-and-comment-operator) (d/leader-keys "ci" 'd/comment-or-uncomment-lines-inverse "cl" 'evilnc-comment-or-uncomment-lines "cp" 'evilnc-comment-or-uncomment-paragraphs "ct" 'evilnc-comment-or-uncomment-to-the-line "cy" 'evilnc-copy-and-comment-lines) :config (defun d/comment-or-uncomment-lines-inverse (&optional arg) "Source: https://git.io/vQKza" (interactive "p") (let ((evilnc-invert-comment-line-by-line t)) (evilnc-comment-or-uncomment-lines arg))))
evil-collection
evil-collection provides evil integration for a bunch of miscellaneous modes for a more consistent experience.
(use-package evil-collection :custom (evil-collection-setup-minibuffer t) (evil-collection-term-sync-state-and-mode-p t) (evil-collection-repl-submit-state 'insert) :init (evil-collection-init))
evil-indent-plus
This package provides evil text objects to manipulate text based on indentation. For example, you can delete the current line and all adjacent lines at the same indentation level.
(use-package evil-indent-plus :general (itomap "i" 'evil-indent-plus-i-indent "I" 'evil-indent-plus-i-indent-up "J" 'evil-indent-plus-i-indent-up-down) (otomap "i" 'evil-indent-plus-a-indent "I" 'evil-indent-plus-a-indent-up "J" 'evil-indent-plus-a-indent-up-down))
evil-lion
evil-lion is an evil-port of vim-lion. It can be used to align text at a particular delimiter.
(use-package evil-lion :general (nvmap "gl" 'evil-lion-left "gL" 'evil-lion-right))
For example:
this text is not aligned at space characters !
Then, press gl<space>:
this text is not aligned at space characters !
evil-matchit
evil-matchit is a port of matchit.vim. It provides a more generic and somewhat
context-aware % binding for jumping between matching brackets, tags, if/else
branches, etc.
(use-package evil-matchit :general (itomap "%" 'evilmi-inner-text-object) (otomap "%" 'evilmi-outer-text-object) (nvmap "%" 'evilmi-jump-items) :config (global-evil-matchit-mode 1))
evil-numbers
evil-numbers provides some operators for incrementing the number at point (or
in selection). I don't actually use it very much, so it's disabled with
org-mode's :tangle no.
(use-package evil-numbers :ensure (:host github :repo "juliapath/evil-numbers") :general (d/leader-keys "n-" 'd/numbers/evil-numbers/dec-at-pt "n=" 'd/numbers/evil-numbers/inc-at-pt) :config (defhydra d/numbers () " ╭─────────╮ │ numbers │ └─────────┘ [_=_] inc [_-_] dec ─────────── " ("=" evil-numbers/inc-at-pt) ("-" evil-numbers/dec-at-pt)))
evil-org
evil-org-mode adds helpful evil-mode keybindings to org-mode for navigating and manipulating org elements.
(use-package evil-org :hook (org-mode . evil-org-mode) :general (nvmap evil-org-mode-map "TAB" 'org-cycle "S-TAB" 'org-cycle) :config (evil-org-set-key-theme) (require 'evil-org-agenda) (evil-org-agenda-set-keys) (nmap evil-org-mode-map [backtab] 'org-shifttab) (d/after 'org-src (define-key org-src-mode-map [remap evil-write] 'org-edit-src-save) (define-key org-src-mode-map [remap evil-save-and-close] (lambda () (interactive) (org-edit-src-save) (org-edit-src-exit))) (define-key org-src-mode-map [remap evil-save-modified-and-close] (lambda () (interactive) (org-edit-src-save) (org-edit-src-exit)))))
evil-cleverparens
I may try this again at some point, but this is disabled for now. I just use electric-pair
(use-package evil-cleverparens :hook (prog-mode . evil-cleverparens-mode))
evil-surround
evil-surround is an evil-mode port of vim-surround. It helps work with surrounding chars better by providing bindings for adding, deleting, or changing surrounding characters (like brackets).
(use-package evil-surround :general (omap "s" 'evil-surround-edit "S" 'evil-Surround-edit) (vmap "S" 'evil-surround-region "gS" 'evil-Surround-region) :config (add-to-list 'evil-surround-operator-alist '(evil-cp-change . change)) (add-to-list 'evil-surround-operator-alist '(evil-cp-delete . delete)))
Ostensibly this was a more minimal integration layer for embrace.el with evil, but I'm not actually sure it's doing anyhting, so it's disabled. See also: evil-embrace.el.
(use-package embrace :after evil-surround :demand t :config (define-advice evil-surround-region (:around (fn beg end type char &optional force-new-line) d/embrace) (if (evil-surround--get-delims char) (funcall fn beg end type char force-new-line) (embrace--add-internal beg end char))) (define-advice evil-surround-delete (:around (fn char &optional outer inner) d/embrace) (if (or (and outer inner) (evil-surround--get-delims char)) (funcall fn char outer inner) (embrace--delete char))))
evil-terminal-cursor-changer
This a package enables changing the cursor type terminals based on the evil-state (like Vim mode), because this can be surprisingly tricky and terminal-dependent.
(use-package evil-terminal-cursor-changer :init (unless (display-graphic-p) (etcc-on)) (add-to-list 'after-make-frame-functions (defun d/maybe-turn-on-cursor-changer (frame) (if (display-graphic-p) (etcc-on)))))
vertico stack
The vertico stack is a minimalist and performant minibuffer completion UI that integrates well with the built-in Emacs completion framework. In some ways it's the spiritual successor to ivy and helm, and a more featureful alternative to the built-in icomplete.
More specifically, it can:
- Suggest and help find emacs commands, file names, buffers, anything…
- Annotate suggestions with extra information
- Provide an alternate ergonomic navigation and search interface (in buffer or external like grep, ripgrep, find, etc.)
I find having a completion framework like this useful for discoverability and ease of use.
vertico
vertico is the main user interface for this stack. It displays completions vertically in the minibuffer.
(use-package vertico :general (nmap vertico-map [escape] 'keyboard-escape-quit "C-/" 'keyboard-escape-quit) (d/leader-keys "SPC" 'execute-extended-command "ff" 'find-file "fa" 'find-alternate-file "fF" 'find-file-other-window "fj" 'project-find-file "hF" 'describe-face) :init (vertico-mode))
consult
consult provides extra search and navigation commands that integrate with completion. I use these commands all the time:
consult-bufferfor switching to buffer with previewconsult-ripgrepfor searching in files with previewconsult-linefor searching the current buffer (also seeconsult-line-multi).consult-yank-popfor kill-ring (like the "clipboard") selection.
Also check out consult-theme for previewing installed themes!
(use-package consult :bind (:map minibuffer-local-map ("M-r" . consult-history)) :general (d/leader-keys "fl" 'consult-locate ;; "hdb" 'counsel-descbinds ;; "iu" 'counsel-unicode-char "sr" 'consult-ripgrep "ss" 'consult-line "sm" 'consult-line-multi "y" 'consult-yank-pop "bb" 'consult-buffer ) :custom (consult-locate-args "locate -i") :config (defun noct-consult-line-evil-history (&rest _) "Add latest `consult-line' search pattern to the evil search history ring. This only works with orderless and for the first component of the search." (when (and (bound-and-true-p evil-mode) (eq evil-search-module 'evil-search)) (let ((pattern (consult--join-regexps (cdr (orderless-compile (car consult--line-history))) 'emacs))) (add-to-history 'evil-ex-search-history pattern) (setq evil-ex-search-pattern (list pattern t t)) (setq evil-ex-search-direction 'forward) (when evil-ex-search-persistent-highlight (evil-ex-search-activate-highlight evil-ex-search-pattern))))) (advice-add #'consult-line :after #'noct-consult-line-evil-history))
orderless
This is basically a fuzzy completion engine, called a completion style in Emacs. It helps you find matches by entering space-separated substrings in any order and/or using regular expressions.
(use-package orderless :custom (completion-styles '(orderless basic)) (completion-category-overrides '(file (styles basic partial-completion))) (orderless-component-separator #'orderless-escapable-split-on-space))
marginalia
marginalia provides details and annotations to minibuffer completions.
(use-package marginalia :init (marginalia-mode))
embark
embark provides alternate actions for minibuffer completions. I won't lie, this one hasn't entirely clicked for me.
I do find embark-export (bound here to C-c C-o) usefull for viewing
consult-ripgrep or consult-line results in their own buffer.
(use-package embark :general (d/leader-keys "iu" 'embark-save-unicode-character) :bind (([S-return] . embark-act) ;; pick some comfortable binding ;; ("C-;" . embark-dwim) ;; good alternative: M-. ("C-h B" . embark-bindings) :map vertico-map ("C-c C-o" . embark-export)) :init ;; Optionally replace the key help with a completing-read interface (setq prefix-help-command #'embark-prefix-help-command) ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc ;; strategy, if you want to see the documentation from multiple providers. ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) :config ;; Hide the mode line of the Embark live/completions buffers (add-to-list 'display-buffer-alist '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil (window-parameters (mode-line-format . none))))) ;; Consult users will also want the embark-consult package. (use-package embark-consult :hook (embark-collect-mode . consult-preview-at-point-mode))
org-mode
org-mode is a special plain-text mode for taking notes, creating documents, literate programming, todo lists, agenda items, and more. It's fantastic, and one of the things that brings people to Emacs.
Of course, this config is written in org-mode. I also use it to publish my blog, take notes with denote (when I remember to), on occasion create presentations… it's a blast. Try org-mode.
Package
(use-package org :ensure nil :general (nmap org-mode-map "ga" 'org-archive-subtree) (mmap org-mode-map "RET" (general-predicate-dispatch nil (d/org-at-openable-item?) 'org-open-at-point (org-at-item-checkbox-p) 'org-toggle-checkbox (org-in-src-block-p) 'org-babel-execute-src-block)) :custom (org-list-allow-alphabetical t)) (use-package org-mouse :ensure nil :after org :demand t)
Defaults
Files
(d/after 'org (setopt org-agenda-text-search-extra-files '(agenda-archives) org-agenda-files '("~/notes.org") org-default-notes-file "~/notes.org" org-archive-location "~/archive.org::* Archive"))
Todo/agenda
(d/after 'org (setopt org-enforce-todo-dependencies t org-enforce-todo-checkbox-dependencies t org-log-done 'time org-log-redeadline 'time org-log-reschedule 'time org-agenda-skip-scheduled-if-done t org-agenda-skip-deadline-if-done t org-agenda-hide-tags-regexp ".*" org-agenda-span 'week org-agenda-deadline-faces '((1.0 . org-warning) (0.5 . org-upcoming-deadline) (0.0 . '(:foreground "#A89984"))) org-todo-keywords '((sequence "TODO(t)" "IN-PROGRESS(p)" "WAITING(w)" "|" "DONE(d)" "CANCELED(c)"))))
Behavior
(d/after 'org ;; Don't reposition the window when cycling (remove-hook 'org-cycle-hook 'org-cycle-optimize-window-after-visibility-change) (setopt org-confirm-babel-evaluate nil org-startup-indented t org-catch-invisible-edits 'error org-insert-heading-respect-content t org-src-window-setup 'current-window org-list-demote-modify-bullet '(("-" . "+") ("+" . "*") ("*" . "-")) org-export-in-background nil org-export-with-author nil org-export-babel-evaluate nil org-html-validation-link nil org-src-tab-acts-natively t org-M-RET-may-split-line nil org-list-use-circular-motion t org-log-into-drawer t org-imenu-depth 5 org-goto-interface 'outline-path-completion org-outline-path-complete-in-steps nil org-link-search-must-match-exact-headline nil org-confirm-elisp-link-function 'y-or-n-p) (define-advice org-babel-execute-src-block (:before (&rest _) d/load-lang) "Load src language on demand. This removes the need to add every language manually to `org-babel-load-languages'. This also implies that any language that supports execution can be executed. Executing src blocks is an active enough action that I'm ok with this." (let ((language (intern (org-element-property :language (org-element-at-point))))) (message "LANG: %s" language) (pcase language ('sh (setq language 'shell)) ('C++ (setq language 'C))) (message "LANG: %s" language) (unless (alist-get language org-babel-load-languages) (add-to-list 'org-babel-load-languages (cons language t)) (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)))) (define-advice org-src--construct-edit-buffer-name (:override (org-buffer-name lang)) (format "%s → %s" org-buffer-name lang))) (d/after 'ob-python (add-to-list 'org-babel-default-header-args:python '(:results . "output")))
Appearance
(d/after 'org ;; appearance (setopt org-src-fontify-natively t org-src-preserve-indentation nil org-edit-src-content-indentation 0 org-fontify-quote-and-verse-blocks t org-hide-emphasis-markers t org-startup-with-inline-images t org-ellipsis " …" ;; This actually slows down org-mode a LOT(?) org-highlight-latex-and-related '(native entities) org-pretty-entities t org-pretty-entities-include-sub-superscripts nil org-hide-leading-stars t org-fontify-done-headline t org-image-actual-width 500 org-latex-packages-alist '(("" "listings") ("" "color") ("" "tabularx")) org-latex-src-block-backend 'listings) ;; latex (setq org-format-latex-options (plist-put org-format-latex-options :scale 2.0)))
Export
(d/after 'org (setf org-html5-fancy t))
Functions
(d/after 'org (defun d/org-agenda-toggle-date (current-line) "Toggle `SCHEDULED' and `DEADLINE' tag in the capture buffer. Source: https://git.io/vQK0I" (interactive "P") (save-excursion (let ((search-limit (if current-line (line-end-position) (point-max)))) (if current-line (beginning-of-line) (goto-char (point-min))) (if (search-forward "DEADLINE:" search-limit t) (replace-match "SCHEDULED:") (and (search-forward "SCHEDULED:" search-limit t) (replace-match "DEADLINE:")))))) (defun d/org-insert-list-item-or-self (char) "If on column 0, insert space-padded CHAR; otherwise insert CHAR. This has the effect of automatically creating a properly indented list leader; like hyphen, asterisk, or plus sign; without having to use list-specific key maps. Source: https://git.io/vQK0s" (if (bolp) (insert (concat char " ")) (insert char))) (defun d/org-swap-tags (tags) "Replace any tags on the current headline with TAGS. The assumption is that TAGS will be a string conforming to Org Mode's tag format specifications, or nil to remove all tags. Source: https://git.io/vQKEE" (let ((old-tags (org-get-tags-string)) (tags (if tags (concat " " tags) ""))) (save-excursion (beginning-of-line) (re-search-forward (concat "[ \t]*" (regexp-quote old-tags) "[ \t]*$") (line-end-position) t) (replace-match tags) (org-set-tags t)))) (defun d/org-set-tags (tag) "Add TAG if it is not in the list of tags, remove it otherwise. TAG is chosen interactively from the global tags completion table. Source: https://git.io/vQKEa" (interactive (list (let ((org-last-tags-completion-table (if (derived-mode-p 'org-mode) (org-uniquify (delq nil (append (org-get-buffer-tags) (org-global-tags-completion-table)))) (org-global-tags-completion-table)))) (completing-read "Tag: " 'org-tags-completion-function nil nil nil 'org-tags-history)))) (let* ((cur-list (org-get-tags)) (new-tags (mapconcat 'identity (if (member tag cur-list) (delete tag cur-list) (append cur-list (list tag))) ":")) (new (if (> (length new-tags) 1) (concat " :" new-tags ":") nil))) (d/org-swap-tags new))) (defun d/org-choose-bullet-type () "Change the bullet type for org lists with a prompt." (interactive) (let ((char (read-char-choice "Bullet type? (-|*|+|1|2|a|b|A|B): " '(?* ?- ?+ ?1 ?2 ?a ?b ?A ?B)))) (pcase char (?1 (org-cycle-list-bullet 3)) (?2 (org-cycle-list-bullet 4)) (?a (org-cycle-list-bullet 5)) (?b (org-cycle-list-bullet 7)) (?A (org-cycle-list-bullet 6)) (?B (org-cycle-list-bullet 8)) (_ (org-cycle-list-bullet (char-to-string char)))))) (defun d/org-at-openable-item? () "Return non-nil if item is openable. (Think link-like)" (let* ((context (org-element-lineage (org-element-context) '(clock footnote-definition footnote-reference headline inlinetask link timestamp) t)) (type (org-element-type context))) (memq type '(footnote-definition footnote-reference headline inlinetask link timestamp)))) (define-advice org-babel-do-key-sequence-in-edit-buffer (:override (key) d/evil-insert) (interactive "kEnter key-sequence to execute in edit buffer: ") (org-babel-do-in-edit-buffer (evil-insert-state) (call-interactively (key-binding (or key (read-key-sequence nil)))))))
Bindings
(d/mode-leader-keys org-mode-map "$" 'org-archive-subtree "'" 'org-edit-special "." 'org-time-stamp "/" 'org-sparse-tree ":" 'd/org-set-tags "-" 'org-decrypt-entry "A" 'org-archive-subtree "P" 'org-set-property "R" 'org-refile "^" 'org-sort "a" 'org-agenda "c" 'org-capture "d" 'org-deadline "g" 'consult-org-heading ;; "G" 'counsel-org-goto-all "l" 'd/org-choose-bullet-type "s" 'org-schedule "ic" 'org-table-insert-column "il" 'org-insert-link "if" 'org-footnote-new "id" 'org-insert-drawer "e" 'org-export-dispatch "b" 'org-babel-tangle "xe" 'org-emphasize "xx" 'org-cut-special "xy" 'org-copy-special "xp" 'org-paste-special ;; tables "tb" 'org-table-blank-field "tc" 'org-table-convert "tdc" 'org-table-delete-column "tf" 'org-table-eval-formula "te" 'org-table-export "tic" 'org-table-insert-column "tih" 'org-table-insert-hline "tiH" 'org-table-hline-and-move "tI" 'org-table-import "tH" 'org-table-move-column-left "tL" 'org-table-move-column-right "tn" 'org-table-create "tN" 'org-table-create-with-table.el "tr" 'org-table-recalculate "ts" 'org-table-sort-lines "ttf" 'org-table-toggle-formula-debugger "tto" 'org-table-toggle-coordinate-overlays "tw" 'org-table-wrap-region) (d/after 'org (d/mode-leader-keys org-src-mode :definer 'minor-mode "'" 'org-edit-src-exit) (d/leader-keys org-src-mode :definer 'minor-mode "fs" 'org-edit-src-save))
Setup
(d/after 'org-agenda (setq org-habit-graph-column 50)) (imap org-capture-mode-mop "C-d" 'd/org-agenda-toggle-date) (nmap org-capture-mode-map "C-d" 'd/org-agenda-toggle-date) (d/after 'org (dolist (char '("+" "-")) (define-key org-mode-map (kbd char) `(lambda () (interactive) (d/org-insert-list-item-or-self ,char)))))
Extras
(use-package org-appear :hook (org-mode . org-appear-mode) :custom (org-appear-autoemphasis t) (org-appear-autoentities t) (org-appear-autolinks t) (org-appear-autokeywords t) (org-appear-autosubmarkers t))
Bindings
(d/leader-keys "." 'abort-recursive-edit "qf" 'delete-frame "qq" 'save-buffers-kill-emacs "td" 'toggle-debug-on-error "tl" 'd/toggle-rlines "&" 'async-shell-command ":" 'eval-expression "r" 'repeat "u" 'universal-argument) (general-def "<f11>" 'kmacro-start-macro-or-insert-counter "<f12>" 'kmacro-end-or-call-macro) (general-def (minibuffer-local-map minibuffer-local-ns-map minibuffer-local-completion-map minibuffer-local-must-match-map minibuffer-local-isearch-map) [?\C-/] 'minibuffer-keyboard-quit [?\C-_] 'minibuffer-keyboard-quit [escape] 'minibuffer-keyboard-quit) (general-def universal-argument-map "SPC u" 'universal-argument-more)
Mode line
(defvar d/selected-window nil) (add-hook 'pre-redisplay-functions (defun d/set-selected-window (_) (unless (minibuffer-window-active-p (selected-window)) (setq d/selected-window (frame-selected-window))))) (defun d/window-active-p () (eq (selected-window) d/selected-window)) (defun d/compact-mode-line-p () (< (window-width) 80)) (defface d/mode-line-dim '((t (:inherit font-lock-comment-face :slant normal))) "Mode line face for dim segments") (defface d/mode-line-half-dim '((t (:foreground "#BDAE93"))) "Mode line face for dim segments") (defface d/mode-line-highlight '((t (:inherit hl-line))) "Mode line mouse face") (defun d/mode-line-face (&optional face inactive-face) (if (d/window-active-p) (or face 'mode-line-active) (or inactive-face 'mode-line-inactive))) (defvar-local d/mode-line-position '((:eval `(:propertize " %4l:" face ,(d/mode-line-face 'd/mode-line-half-dim))) (:eval `(:propertize ,(format "%-2s " (format-mode-line "%c")) face ,(d/mode-line-face 'd/mode-line-half-dim))))) (put 'd/mode-line-position 'risky-local-variable t) (defvar-local d/mode-line-mode `((:propertize (:eval (if (listp mode-name) (car mode-name) mode-name)) mouse-face d/mode-line-highlight local-map ,mode-line-major-mode-keymap) " ")) (put 'd/mode-line-mode 'risky-local-variable t) (defvar-local d/mode-line-anzu '((anzu--state (:eval (propertize (let ((here anzu--current-position) (total anzu--total-matched)) (cond ((eq anzu--state 'replace-query) (format " %d replace " anzu--cached-count)) ((eq anzu--state 'replace) (format " %2d/%d " (1+ here) total)) (anzu--overflow-p (format " %s+ " total)) (t (format " %2s/%d " here total)))) 'face (d/mode-line-face '(:foreground "#83A598"))))))) (put 'd/mode-line-anzu 'risky-local-variable t) (defun d/mode-line-switch-buffer (click) (interactive "e") (when (and (eq (posn-area (event-end click)) 'mode-line) (eq (posn-window (event-end click)) (posn-window (event-start click)))) (with-selected-window (posn-window (event-start click)) (call-interactively #'consult-buffer)))) (defvar-local d/mode-line-buffer-name '((:eval (propertize (cond ((get-buffer-process (current-buffer)) " ") ((buffer-narrowed-p) "▼") (buffer-read-only "⏺") ((buffer-modified-p) "⏺") (t " ")) 'face (d/mode-line-face (cond (buffer-read-only '(:foreground "#D3869B")) ((buffer-modified-p) '(warning)) (t '(:foreground "#B8BB26")))) )) " " (:propertize "%b" face bold help-echo "mouse-1: Switch buffer" mouse-face d/mode-line-highlight local-map (keymap (mode-line keymap (mouse-1 . d/mode-line-switch-buffer)))) " ")) (put 'd/mode-line-buffer-name 'risky-local-variable t) (defvar-local d/mode-line-remote '(:eval (let ((host (file-remote-p default-directory 'host)) (user (file-remote-p default-directory 'user))) (when host (propertize (concat " " user "@" host) 'face (d/mode-line-face '(:foreground "#D3869B" :slant italic) '(:slant italic))))))) (put 'd/mode-line-remote 'risky-local-variable t) (defvar-local d/mode-line-vc '(" " (vc-mode (:eval (let ((value (if (string-prefix-p " Git-" vc-mode) (substring vc-mode 5) vc-mode))) (if (d/window-active-p) value (propertize value 'face nil))))))) (put 'd/mode-line-vc 'risky-local-variable t) (defvar-local d/mode-line-process '(mode-line-process (:eval (propertize (concat (format-mode-line '((mode-line-process ("" mode-line-process " "))))) 'face (d/mode-line-face '(:inherit font-lock-keyword-face :slant normal)))))) (put 'd/mode-line-process 'risky-local-variable t) (defvar-local d/mode-line-coding '((:eval (propertize (pcase (coding-system-eol-type buffer-file-coding-system) (0 "LF") (1 "CRLF") (2 "CR") (_ "")) 'face (d/mode-line-face 'd/mode-line-dim) 'mouse-face 'd/mode-line-highlight 'local-map (let ((map (make-sparse-keymap))) (define-key map [mode-line mouse-1] 'mode-line-change-eol) map))) " " (buffer-file-coding-system (:eval (propertize (upcase (symbol-name (let ((coding-system-name (plist-get (coding-system-plist buffer-file-coding-system) :name))) (pcase coding-system-name ((or 'prefer-utf-8 'undecided) 'utf-8) (_ coding-system-name))))) 'face (d/mode-line-face 'd/mode-line-dim) 'help-echo 'mode-line-mule-info-help-echo 'local-map mode-line-coding-system-map 'mouse-face 'd/mode-line-highlight))))) (put 'd/mode-line-coding 'risky-local-variable t) (defvar d/mode-line-right '(" " (:eval (if (d/compact-mode-line-p) "" mode-line-misc-info)) d/mode-line-process d/mode-line-coding " " d/mode-line-mode )) (put 'd/mode-line-right 'risky-local-variable t) (setq-default mode-line-format `("%e" " " d/mode-line-buffer-name d/mode-line-position d/mode-line-remote d/mode-line-vc d/mode-line-anzu mode-line-format-right-align d/mode-line-right " "))
Help
Built-in
man
(use-package man :ensure nil :general (d/leader-keys "hm" 'man) :init (setq Man-notify-method 'aggressive manual-program (if (executable-find "gman") "gman" "man") Man-sed-command (if (executable-find "gsed") "gsed" "sed")))
woman
(use-package woman :ensure nil :general (d/leader-keys "hw" 'woman) :custom (woman-fill-column 79))
which-key
(use-package which-key :ensure nil :defer 10 :config (which-key-mode))
Packages
helpful
(use-package helpful :custom (helpful-short-filenames t) :general (d/leader-keys "hf" 'helpful-callable "hv" 'helpful-variable "hs" 'helpful-symbol "hk" 'helpful-key) :config (evil-set-initial-state 'helpful-mode 'motion))
elisp-refs
(use-package elisp-refs :config (evil-set-initial-state 'elisp-refs-mode 'motion))
Bindings
(d/leader-keys "hh" 'help-for-help "hV" 'apropos-value "hc" 'describe-char "hM" 'describe-mode "ht" 'describe-text-properties "hn" 'view-emacs-news "hi" 'info "hev" 'version)
Files/Buffers
Settings
(setq uniquify-buffer-name-style 'forward uniquify-strip-common-suffix nil ns-pop-up-frames nil help-window-select t version-control t delete-old-versions t) (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p)
Built-in
tramp
(use-package tramp :ensure nil :custom (tramp-default-method "rsync") (tramp-verbose 2) (tramp-completion-reread-directory-timeout nil) (tramp-auto-save-directory (no-littering-expand-var-file-name "tramp-auto-save/")) (remote-file-name-inhibit-locks t) (tramp-use-scp-direct-remote-copying t) (remote-file-name-inhibit-auto-save-visited t) (tramp-copy-size-limit (* 1024 1024)) (tramp-use-connection-share t) :config (setq tramp-ssh-controlmaster-options (concat "-o ControlPath=/tmp/ssh-ControlPath-%%r@%%h:%%p " "-o ControlMaster=auto " "-o ControlPersist=4h")) (add-to-list 'tramp-default-proxies-alist '("dieggsy\\.com\\'" "\\`root\\'" "/ssh:%h:")) (setq vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)" vc-ignore-dir-regexp tramp-file-name-regexp)) (d/after 'ibuffer (setcdr (last (cdar ibuffer-saved-filter-groups)) `(("Tramp" (filename . ,tramp-file-name-regexp))))) (connection-local-set-profile-variables 'remote-direct-async-process '((tramp-direct-async-process . t))) (connection-local-set-profiles '(:application tramp :protocol "rsync") 'remote-direct-async-process) (connection-local-set-profiles '(:application tramp :protocol "scp") 'remote-direct-async-process) (connection-local-set-profiles '(:application tramp :protocol "ssh") 'remote-direct-async-process) (with-eval-after-load 'compile (remove-hook 'compilation-mode-hook #'tramp-compile-disable-ssh-controlmaster-options))) ;; Could make tramp faster ;; (setq remote-file-name-inhibit-cache nil))
bookmark
(use-package bookmark :ensure nil :general (d/leader-keys "fB" 'bookmark-jump-other-window "fb" 'bookmark-jump))
dired
(use-package dired :ensure nil :general (d/leader-keys "ad" 'dired-other-window) (d/leader-keys wdired-mode-map ;; "fs" 'wdired-finish-edit "fs" (lambda () (interactive) (message "Use ':w' or variant to finish editing "))) (d/mode-leader-keys dired-mode-map "h" 'dired-omit-mode "d" 'dired-du-mode "c" 'dired-collapse-mode) :custom (dired-dwim-target t) ;; (dired-kill-when-opening-new-dired-buffer t) (dired-mouse-drag-files t) (wdired-allow-to-change-permissions 'advanced) :init (setq dired-listing-switches "-DlhA --group-directories-first") (when (member system-type '(darwin berkeley-unix)) (if (executable-find "gls") (setq insert-directory-program "gls") (setq dired-listing-switches "-lhA"))) :config (defun d/dired-remote-listing-switches () (when (file-remote-p dired-directory) (setq-local dired-actual-switches "-lhA"))) (add-hook 'dired-mode-hook #'auto-revert-mode) (add-hook 'dired-mode-hook #'d/dired-remote-listing-switches) (nmap dired-mode-map "~" 'd/dired-home "q" 'd/dired-quit ;; got used to this from ranger "h" 'dired-up-directory "l" 'dired-find-file) (d/after 'dired-async (dired-async-mode 1)) (defun d/dired-quit () (interactive) (let ((prev-nondired (cl-find-if (lambda (spec) (not (eq (buffer-local-value 'major-mode (car spec)) 'dired-mode))) (window-prev-buffers)))) (if prev-nondired (switch-to-buffer (car prev-nondired)) (delete-window)))) (defun d/dired-home () (interactive) (dired "~/")))
dired-x
(use-package dired-x :ensure nil :after dired :demand t :hook (dired-mode . d/dired-maybe-omit) :custom (dired-omit-verbose nil) (dired-omit-files (rx string-start "." (1+ nonl) string-end)) (dired-clean-confirm-killing-deleted-buffers nil) :config (defun d/dired-maybe-omit () (when (directory-files default-directory nil "^[^.]") (dired-omit-mode))) (add-to-list 'dired-omit-extensions ".import.scm") (add-to-list 'dired-omit-extensions ".link"))
ediff
(use-package ediff :ensure nil :custom (ediff-window-setup-function 'ediff-setup-windows-plain) (ediff-split-window-function 'split-window-horizontally) (ediff-diff-options "-w"))
ibuffer
(use-package ibuffer :ensure nil :general (nmap ibuffer-mode-filter-group-map "TAB" 'ibuffer-toggle-filter-group "<backtab>" 'ibuffer-toggle-filter-group "J" 'ibuffer-forward-filter-group "K" 'ibuffer-backward-filter-group) :config (setq ibuffer-saved-filter-groups '(("Default" ("Dired" (mode . dired-mode)) ("ERC" (mode . erc-mode)) ("Help" (or (mode . helpful-mode) (mode . Man-mode) (predicate . (member major-mode ibuffer-help-buffer-modes)))) ("Emacs Package" (filename . ".*/dotfiles/emacs.d/elpaca/.*")) ("Built in" (and (not (mode . eshell-mode)) (filename . "/gnu.*"))) ("Magit" (derived-mode . magit-mode)) ("Shell" (predicate . (member major-mode '(eshell-mode term-mode vterm-mode)))) ("Process" (process)) ("Other" (name . "^*.**$")) ))) (defun d/setup-ibuffer () (ibuffer-switch-to-saved-filter-groups "Default")) (add-hook 'ibuffer-mode-hook #'d/setup-ibuffer) (setq ibuffer-never-show-predicates '("\\*magit-\\(diff\\|process\\):")))
recentf
(use-package recentf :ensure nil :defer 5 :custom (recentf-exclude '(file-remote-p)) (recentf-max-saved-items 50) :config (recentf-mode))
Packages
dired-hacks
dired-open
(use-package dired-open :general (d/mode-leader-keys dired-mode-map "l" 'dired-open-xdg))
dired-rainbow
(use-package dired-rainbow :after dired :demand t :config (dired-rainbow-define-chmod executable-unix "#B8BB26" "-[rw-]+x.*") ;; Most of these are copied from eza (defvar d/image-extensions '("arw" "avif" "bmp" "cbr" "cbz" "cr2" "dvi" "eps" "fodg" "gif" "heic" "heif" "ico" "j2c" "j2k" "jfi" "jfif" "jif" "jp2" "jpe" "jpeg" "jpf" "jpg" "jpx" "jxl" "kra" "krz" "nef" "odg" "orf" "pbm" "pgm" "png" "pnm" "ppm" "ps" "psd" "pxm" "raw" "qoi" "svg" "tif" "tiff" "webp" "xcf" "xpm")) (defvar d/meta-files (rx (group (or (and (or "README" "Readme" "readme") (? "." (* (not " ")))) (and "Dockerfile" (? "." (* (not " ")))) "Brewfile" "bsconfig.json" "BUILD" "BUILD.bazel" "build.gradle" "build.sbt" "build.xml" "Cargo.toml" "CMakeLists.txt" "composer.json" "configure" "Containerfile" "Earthfile" "flake.nix" "Gemfile" "GNUmakefile" "Gruntfile.coffee" "Gruntfile.js" "jsconfig.json" "Justfile" "justfile" "Makefile" "makefile" "meson.build" "mix.exs" "package.json" "Pipfile" "PKGBUILD" "Podfile" "pom.xml" "Procfile" "pyproject.toml" "Rakefile" "RoboFile.php" "SConstruct" "tsconfig.json" "Vagrantfile" "webpack.config.cjs" "webpack.config.js" "WORKSPACE" "id_dsa" "id_ecdsa" "id_ecdsa_sk" "id_ed25519" "id_ed25519_sk" "id_rsa")))) (defvar d/video-extensions '("avi" "flv" "h264" "heics" "m2ts" "m2v" "m4v" "mkv" "mov" "mp4" "mpeg" "mpg" "ogm" "ogv" "video" "vob" "webm" "wmv")) (defvar d/music-extensions '("aac" "m4a" "mka" "mp2" "mp3" "ogg" "opus" "wma")) (defvar d/lossless-music-extensions '("aif" "aifc" "aiff" "alac" "ape" "flac" "pcm" "wav" "wv")) (defvar d/source-extensions '("applescript" "as" "asa" "awk" "c" "c++" "c++m" "cabal" "cc" "ccm" "clj" "cp" "cpp" "cppm" "cr" "cs" "css" "csx" "cu" "cxx" "cxxm" "cypher" "d" "dart" "di" "dpr" "el" "elm" "erl" "ex" "exs" "f" "f90" "fcmacro" "fcscript" "fnl" "for" "fs" "fsh" "fsi" "fsx" "gd" "go" "gradle" "groovy" "gvy" "h" "h++" "hh" "hpp" "hc" "hs" "htc" "hxx" "inc" "inl" "ino" "ipynb" "ixx" "java" "jl" "js" "jsx" "kt" "kts" "kusto" "less" "lhs" "lisp" "ltx" "lua" "m" "malloy" "matlab" "ml" "mli" "mn" "nb" "p" "pas" "php" "pl" "pm" "pod" "pp" "prql" "ps1" "psd1" "psm1" "purs" "py" "r" "rb" "rs" "rq" "sass" "scala" "scm" "scad" "scss" "sld" "sql" "ss" "swift" "tcl" "tex" "ts" "v" "vb" "vsh" "zig")) (defvar d/crypto-extensions '("age" "asc" "cer" "crt" "csr" "gpg" "kbx" "md5" "p12" "pem" "pfx" "pgp" "pub" "sha1" "sha224" "sha256" "sha384" "sha512" "sig" "signature")) (defvar d/document-extensions '("djvu" "doc" "docx" "eml" "fodp" "fods" "fodt" "fotd" "gdoc" "key" "keynote" "numbers" "odp" "ods" "odt" "pages" "pdf" "ppt" "pptx" "rtf" "xls" "xlsm" "xlsx")) (dired-rainbow-define meta (:foreground "#FABD2F" :underline t) d/meta-files) (dired-rainbow-define image "#af5faf" d/image-extensions) (dired-rainbow-define video "#af5fff" d/video-extensions) (dired-rainbow-define music "#8700d7" d/music-extensions) (dired-rainbow-define lossless-music "#8700ff" d/lossless-music-extensions) (dired-rainbow-define crypto "#87afaf" d/crypto-extensions) (dired-rainbow-define document "#8787ff" d/document-extensions) (dired-rainbow-define source (:foreground "#FABD2F" :bold t) d/source-extensions))
dired-collpase
(use-package dired-collapse :config (defvar dired-collapse-ignore-dirs (list "~/Music" "/ssh:")) (defun d/dired-collapse-mode () (unless (cl-find-if (lambda (dir) (string-prefix-p (expand-file-name dir) (expand-file-name dired-directory))) dired-collapse-ignore-dirs) (dired-collapse-mode))))
dired-narrow
(use-package dired-narrow :general (d/mode-leader-keys dired-mode-map "n" 'dired-narrow))
diredfl
(use-package diredfl :hook (dired-mode . diredfl-mode) :custom (diredfl-ignore-compressed-flag nil) :init ;; Set this here because somehow diredfl interferes with this (defun dired-pretty-slashes () (push '("\\\\" . ?\\) prettify-symbols-alist) (prettify-symbols-mode 1)) (add-hook 'dired-mode-hook #'dired-pretty-slashes 100))
dired-du
(use-package dired-du :ensure (:host github :repo "emacsmirror/dired-du" :branch "master") :config (setq dired-du-size-format t))
nerd-icons-dired
Turn off for performance reasons
(use-package nerd-icons-dired :hook (dired-mode . nerd-icons-dired-mode))
whitespace-cleanup-mode
(use-package whitespace-cleanup-mode :defer 15 :config (global-whitespace-cleanup-mode))
wgrep
(use-package wgrep :commands wgrep-change-to-wgrep-mode :custom (wgrep-auto-save-buffer t))
Functions
File/Buffer Manipulation
(defun d/copy-file () "Copy file to another location. Source: https://git.io/vQKES" (interactive) (call-interactively #'write-file))
Switching
(defun d/switch-to-scratch () "Switch to scratch buffer." (interactive) (switch-to-buffer "*scratch*")) (defun d/switch-to-messages () "Switch to *Messages* buffer." (interactive) (switch-to-buffer "*Messages*"))
Narrowing
(defun d/narrow-and-set-normal () "Narrow to the region and, if in a visual mode, set normal mode. Source: https://git.io/vQKEx" (interactive) (narrow-to-region (region-beginning) (region-end)) (if (string= evil-state "visual") (progn (evil-normal-state nil) (evil-goto-first-line)))) (defun d/narrow-to-region-or-subtree () "Narrow to a region, if set, otherwise to an Org subtree, if present. Source: https://git.io/vQKuf" (interactive) (cond ((and mark-active (not (= (region-beginning) (region-end)))) (d/narrow-and-set-normal)) ((derived-mode-p 'org-mode) (org-narrow-to-subtree)) ((derived-mode-p 'prog-mode) (narrow-to-defun)))) (defun d/narrow-dwim () "Narrow to a thing or widen based on context. Attempts to follow the Do What I Mean philosophy. Source: https://git.io/vQKuU" (interactive) (if (buffer-narrowed-p) (widen) (d/narrow-to-region-or-subtree)))
Bindings
(d/leader-keys "bK" 'kill-buffer "bM" 'd/switch-to-messages ;; "br" 'revert-buffer "br" (lambda () (interactive) (message "Use ':e' to revert buffer.")) "bR" 'rename-buffer "bS" 'd/switch-to-scratch "bc" 'clone-indirect-buffer-other-window "bi" 'ibuffer ;; "bk" 'kill-this-buffer "bk" (lambda () (interactive) (message "Use ':bk' to kill buffer.")) "bm" 'kill-matching-buffers "bq" 'kill-buffer-and-window "bv" 'view-mode "fc" 'd/copy-file ;; "fs" 'save-buffer "fs" (lambda () (interactive) (message "Use ':w' to save buffer.")) "nf" 'narrow-to-defun "nn" 'd/narrow-dwim "np" 'narrow-to-page "nr" 'narrow-to-region)
Editing
Settings
(setq-default major-mode 'text-mode fill-column 79 indent-tabs-mode nil ;; tab-width 4 ) (setq sentence-end-double-space nil) (add-hook 'text-mode-hook 'auto-fill-mode)
Tools
Built-in
Input method (mule)
(use-package mule :ensure nil :custom (default-input-method "TeX") :config (defvar d/evil-input-method-title nil) (add-hook 'input-method-activate-hook (defun d/set-evil-input-method-title () (setf d/evil-input-method-title (nth 3 (assoc current-input-method input-method-alist))))) (add-hook 'input-method-deactivate-hook (defun d/unset-evil-input-method-title () (setf d/evil-input-method-title nil))) (defvar-local d/mode-line-mule-info `("" (evil-input-method (:propertize ("" d/evil-input-method-title) help-echo (concat ,(purecopy "Current input method: ") evil-input-method ,(purecopy "\n\ mouse-2: Disable input method\n\ mouse-3: Describe current input method")) local-map ,mode-line-input-method-map mouse-face mode-line-highlight)) ,(propertize "%z" 'help-echo 'mode-line-mule-info-help-echo 'mouse-face 'mode-line-highlight 'local-map mode-line-coding-system-map) (:eval (mode-line-eol-desc))) "Like `mode-line-mule-info' but using `evil-input-method'"))
paren
(use-package paren :ensure nil :hook ((emacs-lisp-mode scheme-mode lisp-mode) . show-paren-mode))
hippie-expand
(use-package hippie-exp :ensure nil :general ("M-/" #'hippie-expand))
electric
(use-package electric :ensure nil :defer 5 :config (electric-indent-mode) (electric-layout-mode))
electric-pair
Okay, I've experimented with frameworks for dealing with parentheses and brackets quite a bit. I've used, tested, or am at least aware of:
- smartparens + evil-smartparens
- lispy + lispyville
- paredit + enhanched-evil-paredit
- evil-cleverparens (maybe I'll try this one again)
- parinfer-rust-mode
And welp. I don't know. They all feel unpolished or heavy and I never quite get
into the flow. Meanwhile, built-in electric-pair-mode stays out of may way,
even if I have to do a bit more manual bracket management. evil-surround helps
some here, and the rest is just good ol' fashined text editing.
(use-package elec-pair :ensure nil :demand t :config (electric-pair-mode))
Packages
corfu
Supposedly better than autocomplete.
(use-package corfu :defer 5 :bind (:map corfu-map ("TAB" . corfu-next) ([tab] . corfu-next) ("S-TAB" . corfu-previous) ([backtab] . corfu-previous) ([return] . corfu-insert)) :custom (corfu-cycle t) (corfu-auto t) (corfu-auto-prefix 1) (global-corfu-minibuffer nil) :config (global-corfu-mode))
cape
(use-package cape :init ;; Add to the global default value of `completion-at-point-functions' which is ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. (add-hook 'completion-at-point-functions #'cape-dabbrev) (add-hook 'completion-at-point-functions #'cape-file) (add-hook 'completion-at-point-functions #'cape-elisp-block) (add-hook 'completion-at-point-functions #'cape-history) (add-hook 'completion-at-point-functions #'cape-elisp-symbol) ;; (add-hook 'completion-at-point-functions #'cape-emoji) (add-hook 'completion-at-point-functions #'cape-keyword) (add-hook 'completion-at-point-functions #'cape-rfc1345) (add-hook 'completion-at-point-functions #'cape-sgml) ;;(add-hook 'completion-at-point-functions #'cape-tex) ;;(add-hook 'completion-at-point-functions #'cape-abbrev) ;;(add-hook 'completion-at-point-functions #'cape-dict) ;;(add-hook 'completion-at-point-functions #'cape-line) )
evil-mc
(use-package evil-mc :config (global-evil-mc-mode))
flyspell
(use-package flyspell :ensure nil :general (d/mode-leader-keys text-mode-map "f" '(:def d/flyspell/body)) :config (defun d/flyspell-add-to-dictionary () "Add word at point to flyspell dictionary. Source: http://tinyurl.com/k8g9sex" (interactive) (let ((current-location (point)) (word (flyspell-get-word))) (when (consp word) (flyspell-do-correct 'save nil (car word) current-location (caddr word) (caddr word) current-location)))) (defhydra d/flyspell () " ╭──────────╮ │ flyspell │ └──────────┴─────────────────────────────────────────────────────── [_>_] goto-next [_c_] correct-next [_a_] auto-correct-next [_b_] buffer [_<_] goto-prev [_C_] correct-prev [_A_] auto-correct-prev [_d_] dict add ─────────────────────────────────────────────────────────────────── " (">" flyspell-goto-next-error) ("<" flyspell-goto-prev-error) ("c" flyspell-correct-next) ("C" flyspell-correct-previous) ("a" flyspell-auto-correct-word) ("A" flyspell-auto-correct-previous-word) ("b" flyspell-buffer) ("d" d/flyspell-add-to-dictionary)))
hungry-delete
(use-package hungry-delete :defer 5 :config (global-hungry-delete-mode))
undo-fu
(use-package undo-fu :ensure (:protocol https))
unfill
(use-package unfill :general (d/leader-keys "xq" 'unfill-toggle) ([remap fill-paragraph] 'unfill-toggle))
yasnippet
(use-package yasnippet :defer 5 :general (d/leader-keys "iy" 'yas-insert-snippet) :config ;; (imap yas-minor-mode-map ;; "SPC" yas-maybe-expand ;; "S-SPC" (lambda () (interactive) (insert " "))) (setq yas-key-syntaxes (remove "w" yas-key-syntaxes)) (yas-global-mode 1))
Modes
cmake-mode
(use-package cmake-mode)
conf-mode
(use-package conf-mode :ensure nil :hook (conf-mode . d/setup-prog-mode) :init (add-to-list 'auto-mode-alist '("\\.service\\'" . conf-mode)))
csv-mode
(use-package csv-mode :mode "\\.csv\\'" :hook (csv-mode . csv-align-mode))
json-mode
(use-package json-mode :mode "\\.json\\'")
markdown-mode
Syntax highlighting for markdown files.
(use-package markdown-mode :mode "\\.md\\'")
yaml-mode
(use-package yaml-mode :mode "\\.yml\\'")
svnwiki-mode
(use-package svnwiki-mode :general (d/mode-leader-keys svnwiki-mode-map "c" 'd/svnwrap-with-code) :mode "\\.svnwiki\\'" :ensure (:repo "https://depp.brause.cc/svnwiki-mode.git") :config (defun d/svnwrap-with-code () (interactive) (let ((bounds (if (region-active-p) (cons (region-beginning) (region-end)) (bounds-of-thing-at-point 'symbol)))) (goto-char (car bounds)) (insert "{{") (goto-char (+ (cdr bounds) 2)) (insert "}}") (goto-char (car bounds)))))
dockerfile-mode
(use-package dockerfile-mode)
olivetti
(use-package olivetti :custom (olivetti-body-width 90))
Bindings
Make indent-rigidly more vimmy.
(general-def indent-rigidly-map "h" 'indent-rigidly-left "l" 'indent-rigidly-right "H" 'indent-rigidly-left-to-tab-stop "L" 'indent-rigidly-right-to-tab-stop)
Leader keys
(d/leader-keys "xii" 'indent-rigidly "xir" 'indent-region "xj" '(:def d/justify/body) "xls" 'sort-lines "xt" '(:def d/transpose/body) "xc" 'count-words "xs" 'd/shorten-url-at-point "xe" 'd/expand-url-at-point "im" 'insert-kbd-macro)
Navigation
Built-in
goto-addr
(use-package goto-addr :ensure nil :hook (((help-mode org-mode text-mode) . goto-address-mode) ((prog-mode conf-mode) . goto-address-prog-mode)))
display-line-numbers
(use-package display-line-numbers :if (version<= "26" emacs-version) :ensure nil :hook ((prog-mode conf-mode) . display-line-numbers-mode) :custom (display-line-numbers-type 'relative) (display-line-numbers-width-start t) (display-line-numbers-grow-only t) :config (defun d/relative-line-numbers () (when display-line-numbers (setf display-line-numbers 'relative))) (defun d/absolute-line-numbers () (when display-line-numbers (setf display-line-numbers t))) (add-hook 'evil-insert-state-entry-hook #'d/absolute-line-numbers) (add-hook 'evil-normal-state-entry-hook #'d/relative-line-numbers) (add-hook 'window-selection-change-functions (defun d/setup-window-selection-change (_) (let* ((old-window (old-selected-window)) (new-window (selected-window)) (new-buffer (window-buffer new-window)) (old-buffer (window-buffer old-window))) (when (not (eql evil-state 'insert)) (when display-line-numbers (setf display-line-numbers 'relative))) (when (not (eq new-buffer old-buffer)) (when old-buffer (with-current-buffer old-buffer (when display-line-numbers (setf display-line-numbers t)))))))))
winner
(use-package winner :ensure nil :defer 15 :general (evil-window-map [right] 'd/winner/winner-redo [left] 'd/winner/winner-undo) :config (winner-mode) (defhydra d/winner () " ╭────────╮ │ winner │ └────────┴──────────────────── [_<left>_] undo [_<right>_] redo ────────────────────────────── " ("<right>" winner-redo) ("<left>" winner-undo)))
hideshow
(use-package hideshow :ensure nil :config (setq hs-allow-nesting t))
xref
(use-package xref :ensure nil :general (d/leader-keys "jD" 'xref-find-definitions-other-window "jd" 'xref-find-definitions "jr" 'xref-find-references) :custom (xref-show-definitions-function #'xref-show-definitions-completing-read))
tab-bar
(use-package tab-bar :ensure nil :general (d/leader-keys "tt" 'tab-switch "tn" 'tab-new "tx" 'tab-close "tr" 'tab-rename) :custom (tab-bar-auto-width-max '(200 20)) (tab-bar-close-button-show nil) (tab-bar-show 1) (tab-bar-separator "") :config ;; for some reason, :defer in the use-package declaration is doing nothing ;; here? Presumably some previous package requires tab-bar. So I'm just ;; directly using the idle timer since I do want to wait for the icons ;; setting. This is a bit jnk. (run-with-idle-timer 5 nil #'tab-bar-mode) (defface d/tab-bar-accent '((t (:background "#BDAE93"))) "Face for active tab accent") (defface d/tab-bar-inactive-accent '((t :inherit 'tab-bar)) "Face for inactive tab accent") (setf tab-bar-tab-name-format-function (defun d/tab-bar-tab-name-format (tab _index) (concat ;; This is a non-breaking space, because I want an accent at the ;; beginning of the tab, but tab-bar-mode uses the first face ;; present (if it's in tab-bar-auto-width-faces) to determine ;; whether to resize the tab and to style the padding. ;; (propertize "" 'face (funcall tab-bar-tab-face-function tab)) ;; (propertize " " ;; 'face (if (eq (car tab) 'current-tab) ;; 'd/tab-bar-accent ;; 'd/tab-bar-inactive-accent)) (propertize (concat " " (alist-get 'name tab)) 'face (funcall tab-bar-tab-face-function tab))))))
Packages
ace-window
(use-package ace-window :general (nmap :keymaps 'override "\\" 'ace-window) (mmap :keymaps 'override "\\" 'ace-window) :config (setq aw-keys (string-to-list "aoeuidhtns") aw-scope 'visible))
avy
(use-package avy :general (d/leader-keys "jc" 'avy-goto-char-2 "jl" 'avy-goto-line "jw" 'avy-goto-word-1 "jt" 'avy-goto-char-timer) :config (setq avy-keys (string-to-list "aoeuidhtns")))
dumb-jump
(use-package dumb-jump :after xref :commands dumb-jump-xref-activate :custom (dumb-jump-prefer-searcher 'rg) (dumb-jump-force-searcher 'rg) :init (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
imenu-anywhere
imenu on steroids.
(use-package imenu-anywhere)
shackle
(use-package shackle :defer 15 :demand t :custom (shackle-rules '((inferior-emacs-lisp-mode :popup t) (inferior-scheme-mode :popup t) ("\\*sly-mrepl" :regexp t :popup t))) :config (shackle-mode))
Bindings
(d/leader-keys "jI" 'imenu-anywhere "jf" 'find-function-other-window "ji" 'imenu "jv" 'find-variable-other-window "jj" 'find-library-other-window)
Appearance
Settings
(xterm-mouse-mode) (setq frame-resize-pixelwise t window-divider-default-places t custom-raised-buttons nil use-dialog-box nil) (setq-default truncate-lines t word-wrap t) (setq-default cursor-in-non-selected-windows nil) (setq highlight-nonselected-windows nil) (add-to-list 'default-frame-alist '(internal-border-width . 8)) (setf frame-title-format nil) (when (eql system-type 'darwin) (setf ns-use-proxy-icon nil) (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)))
Font
(add-to-list 'default-frame-alist '(font . "Iosevka Fixed SS14-11:weight=regular")) (defun d/setup-fonts (&rest ignore) (setq inhibit-compacting-font-caches t use-default-font-for-symbols t) ;; Emoji (cond ((find-font (font-spec :name "Apple Color Emoji")) (set-fontset-font t 'emoji "Apple Color Emoji" nil 'prepend)) ((find-font (font-spec :name "Noto Emoji")) (set-fontset-font t 'emoji "Noto Emoji" nil 'prepend)) ((find-font (font-spec :name "Noto Color Emoji")) (set-fontset-font t 'emoji "Noto Color Emoji" nil 'prepend))) ;; Music (set-fontset-font t '(#x1D100 . #x1d1FF) (font-spec :name "FreeSerif") nil 'prepend)) (when (featurep 'fontset) (d/setup-fonts) (add-to-list 'after-make-frame-functions 'd/setup-fonts))
Built-in
hl-line
(use-package hl-line :ensure nil :hook ((prog-mode emms-browser-mode dired-mode conf-mode) . hl-line-mode) :custom (hl-line-sticky-flag nil))
whitespace
(use-package whitespace :ensure nil :defer 5 :config (setq whitespace-style '(face trailing empty missing-newline-at-eof tabs tab-mark)) (setq whitespace-global-modes '(not erc-mode ses-mode vterm-mode magit-status-mode magit-revision-mode magit-log-mode magit-diff-mode dired-mode)) (global-whitespace-mode))
icons
(use-package icons :ensure nil :custom (icon-preference '(text)))
ansi-color
(use-package ansi-color :ensure nil :custom (ansi-color-bold-is-bright t))
Packages
rainbow-mode
(use-package rainbow-mode :ensure (:host github :repo "emacsmirror/rainbow-mode" :branch "master") :hook (help-mode conf-mode) :config (setq rainbow-x-colors-major-mode-list '(c-mode c++-mode java-mode)))
highlight-numbers
Neat-o
(use-package highlight-numbers :hook (((prog-mode conf-mode) . highlight-numbers-mode) (lisp-mode . (lambda () (highlight-numbers-mode -1))) (json-mode . highlight-numbers--turn-off)))
rainbow-delimiters
Better parentheses coloring
(use-package rainbow-delimiters :hook ((prog-mode conf-mode geiser-repl-mode inferior-scheme-mode inferior-python-mode ielm-mode sly-mrepl-mode) . rainbow-delimiters-mode))
xterm-color
(use-package xterm-color :commands xterm-color-filter :hook ((eshell-before-prompt . d/xterm-color-preserve-properties)) :config (defun d/xterm-color-preserve-properties () (setq xterm-color-preserve-properties t)))
ligature
(use-package ligature :demand t :config ;; Enable all Iosevka ligatures in programming modes (ligature-set-ligatures 'prog-mode '("<---" "<--" "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "<!--" "<==" "<===" "<=" "=>" "=>>" "==>" "===>" ">=" "<=>" "<==>" "<===>" "<====>" "<!---" "<~~" "<~" "~>" "~~>" "::" ":::" "==" "!=" "===" "!==" ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "+:" "-:" "=:" "<******>" "++" "+++")) ;; Enables ligature checks globally in all buffers. You can also do it ;; per mode with `ligature-mode'. (global-ligature-mode t))
comfy-mode
(use-package comfy-mode :ensure (:repo "git@git.sr.ht:~dieggsy/comfy-mode") :init (comfy-mode 1))
Development
Settings
(defun d/auto-fill-only-comments () (setq-local comment-auto-fill-only-comments t)) (add-hook 'prog-mode-hook #'subword-mode) (add-hook 'prog-mode-hook #'auto-fill-mode) (add-hook 'prog-mode-hook #'d/auto-fill-only-comments) (add-to-list 'auto-mode-alist '("PKGBUILD" . sh-mode)) (add-to-list 'auto-mode-alist '("\\.hook\\'" . conf-mode))
Misc
flycheck
(use-package flycheck :hook (python-mode . flycheck-mode) :general (d/mode-leader-keys prog-mode-map "f" '(:def d/flycheck/body)) :config (define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow [#b10000000 #b01000000 #b11100000 #b00010000 #b11111000 #b00000100 #b11111110 #b00000100 #b11111000 #b00010000 #b11100000 #b01000000 #b10000000]) (defhydra d/flycheck (:body-pre (progn (flycheck-mode 1) (flycheck-list-errors)) :post (quit-windows-on "*Flycheck errors*")) " ╭──────────╮ │ flycheck │ └──────────┴──────── [_>_] next [_H_] first [_<_] prev [_L_] last ──────────────────── " ("f" flycheck-error-list-set-filter) (">" flycheck-next-error) ("<" flycheck-previous-error) ("H" flycheck-first-error) ("L" (progn (goto-char (point-max)) (flycheck-previous-error)))))
hl-todo
(use-package hl-todo :hook (prog-mode . hl-todo-mode))
Language Support
treesit-auto
(use-package treesit-auto :demand t :custom (treesit-auto-install 'prompt) :config (treesit-auto-add-to-auto-mode-alist 'all) (global-treesit-auto-mode) (add-to-list 'treesit-auto-recipe-list (make-treesit-auto-recipe :lang 'c :ts-mode 'c-ts-mode :remap 'c-mode :url "https://github.com/tree-sitter/tree-sitter-c" :requires 'cpp :abi14-revision "v0.23.0" :ext "\\.c\\'") ) (add-to-list 'treesit-auto-recipe-list (make-treesit-auto-recipe :lang 'cpp :ts-mode 'c++-ts-mode :remap 'c++-mode :requires 'c :url "https://github.com/tree-sitter/tree-sitter-cpp" :revision "v0.22.0" ;; BUG: newer grammar breaks syntax highlighting in `c++-ts-mode' :abi14-revision "v0.23.0" :ext "\\.cpp\\'")) )
C++
Packages
irony
(use-package irony :hook ((c++-mode . irony-mode) (irony-mode . irony-cdb-autosetup-compile-options)))
company-irony
(use-package company-irony :after irony :demand :init (d/after 'company '(add-to-list 'company-backends 'company-irony)))
lsp-mode
(use-package lsp-mode :hook (c++-mode c-mode))
cquery
(use-package cquery :hook (c++-mode . lsp-cquery-enable) :config (setq cquery-executable "/usr/bin/cquery"))
ccls
(use-package ccls :hook (c++-mode . d/enable-ccls) :config (defun d/enable-ccls () (require 'ccls) (lsp)))
lsp-ui
(use-package lsp-ui :hook (lsp-mode . lsp-ui-mode))
Setup
(defalias 'cpp-mode 'c++-mode) (d/after 'cc-mode (setq-default c-basic-offset 4) (c-set-offset 'case-label '+))
Python
Built-in
(use-package python :ensure nil :hook (python-mode . eglot-ensure) :config (d/after 'eglot (add-to-list 'eglot-server-programs `(python-mode . ("uvx" "--from" "basedpyright" "basedpyright-langserver" "--" "--stdio")))))
Packages
pyenv-mode
(use-package pyenv-mode :config (pyenv-mode))
pyenv-mode-auto
(use-package pyenv-mode-auto :after pyenv-mode :demand t)
Bindings
(d/mode-leader-keys python-mode-map "eb" 'python-shell-send-buffer "ef" 'python-shell-send-defun "er" 'python-shell-send-region "eF" 'python-shell-send-file "es" 'python-shell-send-statement)
Setup
(defun d/setup-python () (set (make-local-variable 'comment-inline-offset) 2)) (add-hook 'python-mode-hook #'d/setup-python) (d/after 'python (setq python-shell-completion-native-enable nil))
Lisps
Packages
request
(use-package request) (use-package request-deferred)
flycheck-package
(use-package flycheck-package :after flycheck :demand t :config (flycheck-package-setup))
macrostep
(use-package macrostep :general (d/mode-leader-keys (emacs-lisp-mode-map lisp-mode-map) "m" 'macrostep-expand))
highlight-quoted
(use-package highlight-quoted :hook ((lisp-mode sly-mrepl-mode emacs-lisp-mode) . highlight-quoted-mode))
lispyville
(use-package lispyville ;; :hook ((emacs-lisp-mode scheme-mode lisp-mode) . lispyville-mode) :custom (lispyville-key-theme '(operators c-w c-u prettify (atom-movement t) slurp/barf-lispy additional additional-movement additional-insert)))
Functions
(defun d/eval-surrounding-sexp (levels) "Eval sexp around point, specifying depth with LEVELS. Source: http://tinyurl.com/le6wxuo" (interactive "p") (save-excursion (up-list (abs levels)) (eval-last-sexp nil)))
Bindings
(d/mode-leader-keys emacs-lisp-mode-map "eb" 'eval-buffer "ef" 'eval-defun "er" 'eval-region "es" 'd/eval-surrounding-sexp "el" 'eval-last-sexp)
Setup
(defun d/setup-lisp () (hs-minor-mode) (d/after 'evil-surround (push '(? . ("`" . "'")) evil-surround-pairs-alist))) (add-hook 'lisp-mode-hook #'d/setup-lisp) (add-hook 'emacs-lisp-mode-hook #'d/setup-lisp)
Haskell
(use-package haskell-mode :mode "\\.hs\\'" :config (setq haskell-indentation-layout-offset 4 haskell-indentation-left-offset 4 haskell-indentation-ifte-offset 4))
Racket
(use-package racket-mode)
Rust
rust-mode
(use-package rust-mode)
racer
(use-package racer :hook (rust-mode . racer-mode))
ob-rust
(use-package ob-rust)
Scheme
Built-in
(use-package scheme :ensure nil :mode ("\\.sld\\'" . scheme-mode) :mode ("\\.egg\\'" . scheme-mode) :mode ("\\.release-info\\'" . scheme-mode) :interpreter (("chicken-scheme" . scheme-mode) ("csi" . scheme-mode)) :general (d/mode-leader-keys scheme-mode-map "eb" 'd/scheme-send-buffer "ef" 'scheme-send-definition "er" 'scheme-send-region "el" 'scheme-send-last-sexp "es" 'd/scheme-send-surrounding-sexp) (nmap scheme-mode-map "C-c C-c" 'd/scheme-compile-region-or-definition) :init (defvar d/scheme-extra-keywords ;; Function-like keywords `((,(rx "(" (group (or "define-for-syntax" "define-inline" "define-external" "define-constant" "define-foreign-variable")) symbol-end (* space) (? "(") (? (group (1+ word)))) (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)) ;; Make consistent with define-syntax (,(rx "(" (group "define-syntax-rule") symbol-end (* space) (? "(") (? (group (1+ word)))) (1 font-lock-keyword-face) (2 font-lock-variable-name-face nil t)) ;; Highlight types (,(rx "(" (group "define-foreign-type") symbol-end (* space) (? (group (1+ word))) (? (* space)) (? (group (1+ word)))) (1 font-lock-keyword-face) (2 font-lock-type-face) (3 font-lock-type-face)) ;; CHICKEN-style keywords ("\\<\\sw+:\\>" . 'font-lock-builtin-face) ;; Hashbang ("\\`\\(#!.*/\\)\\([^ \t\n]+\\)\\( .*\\)?" (1 font-lock-comment-face) (2 font-lock-keyword-face t) (3 font-lock-comment-face t t)) ;; #!key, #!optional, #!rest etc. ("\\<#!\\sw+\\>" . 'font-lock-type-face))) (font-lock-add-keywords 'scheme-mode d/scheme-extra-keywords t) (font-lock-add-keywords 'inferior-scheme-mode d/scheme-extra-keywords t) (defvar d/scheme-simple-keywords `((,(format "[[(]%s\\>" (regexp-opt '("and-let*" "cut" "cute" "define-record-type" "define-values" "letrec*" "set!" "syntax-case" "unless" "when" "assume" "compiler-typecase" "cond-expand" "condition-case" "declare" "define-interface" "define-record" "define-specialization" "define-type" "fluid-let" "foreign-lambda" "foreign-lambda*" "foreign-primitive" "foreign-safe-lambda" "foreign-safe-lambda*" "functor" "handle-exceptions" "let-location" "let-optionals" "let-optionals*" "letrec-values" "module" "define-foreign-record-type" "foreign-value" "foreign-declare" "begin-for-syntax" "define-for-syntax" "include-relative" "import-for-syntax" ;; matchable "match" "match-lambda" "match-lambda*" "match-let" "match-let*" "match-letrec" ;; miscmacros "dotimes" "ecase" "select" "repeat" "while" ;; test "test-assert" "test-begin" "test-end" "test" "test-error" "test-group") 1)) . 1))) (font-lock-add-keywords 'scheme-mode d/scheme-simple-keywords t) (font-lock-add-keywords 'inferior-scheme-mode d/scheme-simple-keywords t) :config (defun d/scheme-send-buffer () (interactive) (scheme-send-region (point-min) (point-max))) (defun d/scheme-send-surrounding-sexp (levels) (interactive "p") (save-excursion (up-list (abs levels)) (scheme-send-last-sexp))) (defun d/scheme-compile-region-or-definition () (interactive) (if (region-active-p) (scheme-compile-region (region-beginning) (region-end)) (scheme-compile-definition))) (setq scheme-program-name "csi -q") ;; chicken core (put 'module 'scheme-indent-function 2) (put 'functor 'scheme-indent-function 2) (put 'begin-for-syntax 'scheme-indent-function 0) (put 'and-let* 'scheme-indent-function 1) (put 'let-optionals 'scheme-indent-function 1) (put 'let-optionals* 'scheme-indent-function 1) (put 'letrec-values 'scheme-indent-function 1) (put 'case-lambda 'scheme-indent-function 0) (put 'catch 'scheme-indent-function 'defun) (put 'assume 'scheme-indent-function 1) (put 'cond-expand 'scheme-indent-function 0) (put 'cut 'scheme-indent-function 1) (put 'cute 'scheme-indent-function 1) (put 'set-read-syntax! 'scheme-indnet-function 1) (put 'set-sharp-read-syntax! 'scheme-indnet-function 1) (put 'condition-case 'scheme-indent-function 1) (put 'handle-exceptions 'scheme-indent-function 2) (put 'with-exception-handler 'scheme-indent-function 1) (put 'with-input-from-pipe 'scheme-indent-function 1) (put 'with-output-to-pipe 'scheme-indent-function 1) (put 'compiler-typecase 'scheme-indent-function 1) (put 'foreign-lambda 'scheme-indent-function 2) (put 'foreign-lambda* 'scheme-indent-function 2) (put 'foreign-safe-lambda 'scheme-indent-function 2) (put 'foreign-safe-lambda* 'scheme-indent-function 2) (put 'foreign-primitive 'scheme-indent-function 2) (put 'let-location 'scheme-indent-function 1) (put 'with-error-output-to-port 'scheme-indent-function 1) ;; matchable (put 'match 'scheme-indent-function 1) (put 'match-let 'scheme-indent-function 1) (put 'match-let* 'scheme-indent-function 1) (put 'match-letrec 'scheme-indent-function 1) (put 'match-lambda 'scheme-indent-function 0) (put 'match-lambda* 'scheme-indent-function 0) ;; miscmacros (put 'while 'scheme-indent-function 1) (put 'select 'scheme-indent-function 1) (put 'dotimes 'scheme-indent-function 1) (put 'repeat 'scheme-indent-function 1) (put 'ecase 'scheme-indent-function 1) ;; test (put 'test-assert 'scheme-indent-function 1) (put 'test-error 'scheme-indent-function 1) (put 'test 'scheme-indent-function 1) (put 'test-group 'scheme-indent-function 1) ;; spiffy (put 'with-headers 'scheme-indent-function 1) (put 'with-egg 'scheme-indent-function 1))
Packages
chicken-doc.el
(use-package chicken-doc :general (d/mode-leader-keys scheme-mode-map "d" 'chicken-doc-describe) (d/mode-leader-keys inferior-scheme-mode-map "d" 'chicken-doc-describe) :ensure (:repo "https://depp.brause.cc/chicken-doc.el.git"))
Lisp
Built-in
(use-package lisp-mode :ensure nil :mode "\\.\\(tdf\\|tv-tic\\|tv-diag\\|tv-desc\\|cv-desc\\)\\'")
Packages
sly
(use-package sly :general (d/leader-keys "as" 'd/sly) (nmap sly-mode-map "gd" 'd/sly-edit-or-evil-goto-definition) (:keymaps 'sly-mrepl-mode-map "M-r" 'consult-history) (imap sly-mrepl-mode-map [up] 'sly-mrepl-previous-input-or-button [down] 'sly-mrepl-next-input-or-button) (d/mode-leader-keys lisp-mode-map "M" 'sly-macroexpand-1-inplace "eb" 'sly-eval-buffer "ef" 'sly-eval-defun "er" 'sly-eval-region ;; "es" 'd/eval-surrounding-sexp "el" 'sly-eval-last-expression) :config (defun d/sly () (interactive) (let ((current-prefix-arg '-)) (call-interactively #'sly))) (setq sly-lisp-implementations '((sbcl ("sbcl" "--dynamic-space-size" "8912")) (alisp ("alisp")) (ecl ("ecl")) (clisp ("clisp"))) sly-contribs (append '(sly-tramp sly-macrostep) sly-contribs)) (d/after 'sly-mrepl (cl-pushnew 'ansi-color-apply sly-mrepl-output-filter-functions)) (defun d/sly-edit-or-evil-goto-definition () (interactive) (condition-case nil (call-interactively 'sly-edit-definition) (error (evil-goto-definition)))) (define-advice sly--mode-line-format (:override () d/sly-mode-line) (let* ((conn (sly-current-connection)) (conn (and (process-live-p conn) conn)) (name (or (and conn (sly-connection-name conn)) "*")) (pkg (sly-current-package)) (package-name (and pkg (sly--pretty-package-name pkg)))) `((:eval (propertize ,name 'face (when (d/window-active-p) '(:inherit font-lock-keyword-face :slant normal)))) (:eval (propertize ":" 'face (when (d/window-active-p) '(:inherit font-lock-comment-face :slant normal)))) (:eval (propertize ,(or package-name "*") 'face (when (d/window-active-p) '(:inherit font-lock-constant-face :slant normal)))))))) (use-package sly-macrostep)
slime
(use-package slime :general (d/leader-keys "as" 'd/slime) (:keymaps 'slime-repl-mode-map "M-r" 'consult-history) (imap slime-repl-mode-map [up] 'slime-repl-previous-input [down] 'slime-repl-next-input) :hook (lisp-mode . slime-mode) :config (defun d/slime () (interactive) (let ((current-prefix-arg '-)) (call-interactively #'slime))) (d/after 'slime-presentations (define-key slime-presentation-map [return] 'slime-inspect-presentation-at-point) (define-key slime-presentation-map [mouse-1] 'slime-inspect-presentation-at-point)) (require 'slime-macrostep) (add-to-list 'mode-line-misc-info ;; slime-repl-mode doesn't actually set the variable ;; slime-repl-mode for some reason '(:eval (when (or (eql major-mode 'slime-repl-mode) slime-mode) (concat (slime-modeline-string) " ")))) (setq inferior-lisp-program (executable-find "sbcl") slime-lisp-implementations '((sbcl ("sbcl" "--dynamic-space-size" "8912")) (alisp ("alisp")) (ecl ("ecl")) (clisp ("clisp"))) slime-contribs (append '(slime-macrostep slime-tramp slime-cl-indent) slime-contribs)) (define-advice slime-maybe-complete-as-filename (:override ()) "Don't hook into slime's file completion mechanism. It's a bit too agressive and cape covers this anyway." nil)) (d/after 'consult (add-to-list 'consult-mode-histories '(slime-repl-mode slime-repl-input-history slime-repl-input-history-position nil))) ;; (use-package slime-company ;; :after slime ;; :autoload company-slime ;; :custom ;; (slime-company-completion 'fuzzy) ;; :init ;; (d/after 'cape ;; (add-hook 'slime-repl-mode-hook (lambda () (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-slime)))) ;; (add-hook 'slime-mode-hook (lambda () (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-slime))))))
cl-fancy
(use-package cl-fancy :ensure (:repo "git@git.sr.ht:~dieggsy/cl-fancy") :hook lisp-mode :after lisp-mode :demand t)
Functions
(defun d/remote-alisp () (interactive) (let* ((remote-host (read-from-minibuffer "Remote host: ")) (name (generate-new-buffer-name "*remote-alisp*")) (forward-proc (concat name "_forward"))) (make-process :name name :buffer name :command `("ssh" ,remote-host "alisp --qq -L /user/dmundo/remote-alisp/start-slynk.lisp") :filter (lambda (proc output) (when (string-match ";; Slynk started at port: \\([[:digit:]]+\\)" output) (let ((port (match-string 1 output))) (make-process :name forward-proc :command `("ssh" ,(format "-L%s:localhost:%s" port port) ,remote-host "echo port-ready") :filter (lambda (proc output) (when (string-match-p "port-ready" output) (sly-connect "localhost" (string-to-number port))))))) (internal-default-process-filter proc output)))))
Ocaml
tuareg
(use-package tuareg :general (d/mode-leader-keys tuareg-mode-map "es" 'tuareg-eval-phrase "eb" 'tuareg-eval-buffer "er" 'tuareg-eval-region))
dune
(use-package dune)
merlin
(use-package merlin :hook (tuareg-mode . merlin-mode))
vimrc-mode
(use-package vimrc-mode)
web-mode
(use-package web-mode :mode ("\\.html?\\'" "\\.xml\\'" "\\.launch\\'") :config (setq web-mode-markup-indent-offset 2))
lua-mode
(use-package lua-mode)
fish-mode
(use-package fish-mode)
java
Packages
(use-package eglot-java :hook (java-mode . eglot-java-mode) :custom (eglot-java-user-init-opts-fn 'd/eglot-java-init) :init (defun d/eglot-java-init (server eglot-java-eclipse-jdt) '(:settings (:java (:configuration :home "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" ) ;; (:home "/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home") ))))
gitlab-ci
(use-package gitlab-ci-mode)
robot
(use-package robot-mode :config (defun d/setup-robot () (make-local-variable 'whitespace-style) (add-to-list 'whitespace-style 'spaces 'append) (add-to-list 'whitespace-style 'space-mark 'append)) (add-hook 'robot-mode-hook #'d/setup-robot) (d/after 'eglot (add-to-list 'eglot-server-programs '(robot-mode . ("robotframework_ls")))))
Version Control
diff-hl
(use-package diff-hl :defer 10 :custom (diff-hl-fringe-bmp-function 'd/diff-hl-bitmap) (diff-hl-dired-fringe-bmp-function 'd/diff-hl-bitmap) :init (define-fringe-bitmap 'd/diff-hl-bitmap (make-vector 29 #b00001111)) (defun d/diff-hl-bitmap (type pos) 'd/diff-hl-bitmap) :config (global-diff-hl-mode) (add-hook 'dired-mode-hook #'diff-hl-dired-mode) (d/after 'magit (add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)))
magit
Like git, for emacs. But cooler. (Just trust me on this one.)
(use-package transient :custom (transient-default-level 7)) (use-package magit :general (d/leader-keys "gB" 'magit-blame "gC" 'magit-clone "gL" 'magit-log-buffer-file "ga" 'magit-submodule-add "gb" 'magit-branch "gc" 'magit-checkout "gf" 'magit-find-file "gl" 'magit-log-all "gs" 'magit-status "gp" 'magit-file-dispatch) :init (setq vc-follow-symlinks t auto-revert-check-vc-info t) :custom (magit-repository-directories '(("~/dotfiles" . 0) ("~/src" . 1) ("~/src/chicken/chicken-eggs/" . 1))) (magit-diff-refine-hunk t) (git-commit-summary-max-length 50) (git-commit-major-mode 'org-mode) (magit-tramp-pipe-stty-settings 'pty) (magit-commit-show-diff nil) (magit-branch-direct-configure nil) (magit-tramp-pipe-stty-settings 'pty) (magit-process-apply-ansi-colors t) :config (defun d/setup-git-commit () (setq-local org-hide-emphasis-markers nil org-pretty-entities nil)) (add-hook 'git-commit-mode-hook #'d/setup-git-commit) (add-hook 'git-commit-setup-hook #'git-commit-turn-on-flyspell))
magit-todos
(use-package magit-todos :after magit :config (magit-todos-mode 1))
smerge
(use-package smerge-mode :ensure nil :general (nmap smerge-mode-map "]]" 'd/smerge/smerge-next "[[" 'd/smerge/smerge-prev) (smerge-mode-map "C-<return>" 'smerge-keep-current) :config (defhydra d/smerge () " ╭────────╮ │ smerge │ └────────┴────────────────────────────── [_]]_] next [_>_] keep-lower [_._] keep-base [_[[_] prev [_<_] keep-upper [_._] keep-all ──────────────────────────────────────── " ("]]" smerge-next) ("[[" smerge-prev) (">" smerge-keep-lower) ("<" smerge-keep-upper) ("." smerge-keep-base) ("a" smerge-keep-all)))
Tools
proced
(use-package proced :ensure nil :custom (proced-auto-update-interval 1) (proced-format 'medium) (proced-filter 'user) (proced-auto-update-flag 'visible) (proced-enable-color-flag t) (proced-descend t))
comint
(setq comint-prompt-read-only t comint-highlight-input nil)
grep
(use-package grep :ensure nil :config (grep-apply-setting 'grep-use-null-device nil) (grep-apply-setting 'grep-template "rg --color=auto --no-heading -nH --null -g '<F>' -e <R>") (grep-apply-setting 'grep-command "rg --color=auto --no-heading -nH --null -e ") (grep-apply-setting 'grep-find-command '("find . -type f -exec rg --color=auto --no-heading -nH --null -e \\{\\} +" . 65)) (grep-apply-setting 'grep-find-template "find -H <D> <X> -type f <F> -exec rg --no-heading -nH --null -e <R> \\{\\} +"))
calc
And it's amazing, to be quite honest. "The poor man's Mathematica", I once heard (and can confirm).
(use-package calc :ensure nil :general (d/leader-keys "ac" 'calc-dispatch) (emap calc-mode-map "x" (lambda () (interactive) (counsel-M-x "^calc-"))) :init (add-hook 'calc-embedded-mode-hook #'d/calc-embedded-set-open-close) :config (evil-set-initial-state 'calc-mode 'emacs) (setq calc-multiplication-has-precedence nil calc-symbolic-mode t) (defun d/calc-embedded-set-open-close () (when comment-start (setq-local calc-embedded-open-mode (concat comment-start " "))) (when comment-end (setq-local calc-embedded-close-mode (concat comment-end "\n")))))
async
(use-package async)
htmlize
(use-package htmlize)
eshell
Builtin
(use-package eshell :ensure nil :custom-face (eshell-prompt ((t (:foreground unspecified :inherit default)))) (eshell-ls-backup ((t :foreground unspecified :inherit dired-ignored))) :custom (eshell-modules-list '(eshell-alias eshell-banner eshell-basic eshell-cmpl eshell-dirs eshell-elecslash eshell-extpipe eshell-glob eshell-hist eshell-ls eshell-pred eshell-prompt eshell-script eshell-term eshell-unix)) (eshell-scroll-to-bottom-on-input t) (eshell-hist-ignore-dups t) (eshell-history-size 20000) (eshell-destroy-buffer-when-process-dies t) (eshell-cmpl-compare-entry-function #'string-lessp) :init (defun d/consult-completion () (setq-local completion-in-region-function #'consult-completion-in-region)) (add-hook 'eshell-mode-hook #'d/consult-completion) :config (require 'dired) (require 'diredfl) (defun d/disable-corfu () (corfu-mode -1)) (add-hook 'eshell-mode-hook #'d/disable-corfu) (d/after 'em-hist (define-key eshell-hist-mode-map (kbd "M-r") #'consult-history)) (d/after 'em-term (dolist (visual-command '("ncdu")) (add-to-list 'eshell-visual-commands visual-command))) (defalias #'eshell/clear #'eshell/clear-scrollback) (d/after 'em-ls (defun d/esh-ls-match-meta (file attrs) (string-match-p (concat (rx bos) d/meta-files (rx eos)) file)) (defun d/esh-ls-match-image (file attrs) (string-match-p (concat "\\." (regexp-opt d/image-extensions) "\\'") file)) (defun d/esh-ls-match-video (file attrs) (string-match-p (concat "\\." (regexp-opt d/video-extensions) "\\'") file)) (defun d/esh-ls-match-music (file attrs) (string-match-p (concat "\\." (regexp-opt d/music-extensions) "\\'") file)) (defun d/esh-ls-match-lossless-music (file attrs) (string-match-p (concat "\\." (regexp-opt d/lossless-music-extensions) "\\'") file)) (defun d/esh-ls-match-crypto (file attrs) (string-match-p (concat "\\." (regexp-opt d/crypto-extensions) "\\'") file)) (defun d/esh-ls-match-document (file attrs) (string-match-p (concat "\\." (regexp-opt d/document-extensions) "\\'") file)) (defun d/esh-ls-match-source (file attrs) (string-match-p (concat "\\." (regexp-opt d/source-extensions) "\\'") file)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-meta . dired-rainbow-meta-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-image . dired-rainbow-image-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-video . dired-rainbow-video-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-music . dired-rainbow-music-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-lossless-music . dired-rainbow-lossless-music-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-crypto . dired-rainbow-crypto-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-document . dired-rainbow-document-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-source . dired-rainbow-source-face))) (define-advice eshell-send-input (:before (_) d/expand-abbrev) (expand-abbrev)) (defun d/eshell-expand-abbrev () "Expand command abbrev sorta like fish abbreviations. This should just be a copy of abbrev--default-expand that adds the condition of only expanding abbrevs at the beginning of the line." (pcase-let ((`(,sym ,name ,wordstart ,wordend) (abbrev--before-point))) (when (and sym (save-excursion (goto-char wordstart) (forward-comment (- (buffer-size))) (or (save-excursion (backward-char 1) (looking-at-p "|")) (save-excursion (forward-char 1) (= (point) (line-beginning-position)))))) (abbrev--default-expand)))) (defun d/eshell-abbrevs () (setq-local abbrev-expand-function #'d/eshell-expand-abbrev)) (add-hook 'eshell-mode-hook #'abbrev-mode) (add-hook 'eshell-mode-hook #'d/eshell-abbrevs))
egp
(use-package egp :after eshell :commands egp-theme :ensure nil :custom (eshell-prompt-function #'egp-theme))
esh-ls-colors
(use-package esh-ls-colors :after em-ls :demand t :ensure nil)
esh-more-commands
(use-package esh-more-commands :after eshell :demand t :ensure nil)
eshell-syntax-highlighting
(use-package eshell-syntax-highlighting :after eshell :demand t :hook eshell-mode)
esh-autosuggest
(use-package esh-autosuggest :hook (eshell-mode . esh-autosuggest-mode))
eat
(use-package eat :after eshell :demand t :init (with-eval-after-load 'eshell (eat-eshell-mode +1) (eat-eshell-visual-command-mode +1)))
denote
(use-package denote :custom (denote-directory (expand-file-name "~/denote/")) (denote-prompts '(subdirectory title keywords))) (use-package denote-org) (use-package consult-denote)
Emacs Enhancements
crux
(use-package crux :general (d/leader-keys "TAB" 'crux-switch-to-previous-buffer "fd" 'crux-delete-file-and-buffer "fr" 'crux-rename-file-and-buffer "bs" 'crux-sudo-edit) (d/mode-leader-keys emacs-lisp-mode-map "eR" 'crux-eval-and-replace) :config (crux-with-region-or-line eval-region) (crux-with-region-or-buffer indent-region) (crux-with-region-or-buffer untabify) (crux-with-region-or-buffer tabify) (crux-with-region-or-buffer fill-region))
persistent-scratch
(use-package persistent-scratch :defer 10 :config (with-current-buffer "*scratch*" (emacs-lisp-mode)) (persistent-scratch-setup-default))
Web
(defun d/paste-region-or-buffer () (interactive) (let* ((buffer (current-buffer)) (region-active (region-active-p)) (start (if region-active (region-beginning) (point-min))) (end (if region-active (region-end) (point-max))) (file-name (buffer-file-name)) (name (and file-name `("-n" ,file-name)))) (with-temp-buffer (let ((temp-buffer (current-buffer))) (with-current-buffer buffer (call-process-region start end "hut" nil temp-buffer nil "paste" "create"))) (kill-new (buffer-substring (point-min) (point-max))))))
ERC
(use-package erc :ensure nil :custom (erc-hide-list '("JOIN" "PART" "QUIT")) (erc-modules '(autojoin button completion fill imenu irccontrols keep-place list match menu move-to-prompt netsplit networks nicks notifications readonly ring scrolltobottom spelling stamp track)) :init (defun erc-connect () (interactive) (erc-tls :nick "dieggsy" :server "chat.sr.ht" :port 6697 :user "dieggsy/irc.libera.chat" :password (car (process-lines "op" "read" "op://Private/Sourcehut/chat.sr.ht token")))))
Local
At the end I load a file called local.el, which is local to each machine and
not version controlled. I use this for simple configuration that isn't shared,
like setting font sizes for particular displays or other computer-specific
configuration (e.g. home desktop, personal server, work laptop, work server).
(let ((local-config (locate-user-emacs-file "local.el"))) (when (file-exists-p local-config) (load local-config)))
Local vars
# Local Variables: # eval: (add-hook 'after-save-hook 'org-babel-tangle 'append 'local) # End: