Compare commits

...

12 commits

Author SHA1 Message Date
981c1eeb51
fix: delay loading of function override 2025-10-22 13:39:51 -04:00
49d59cfa9f
feat: update Nix flake file template 2025-10-22 13:38:07 -04:00
a1c0bed2db
docs: prose edits 2025-10-22 13:37:59 -04:00
102b0fb19c
feat: add Markdown polymode 2025-10-22 13:33:18 -04:00
17a9aed526
feat: generalize Nix module file template 2025-10-22 13:32:46 -04:00
7a5a588385
compat: point pass to the correct store directory 2025-10-22 13:28:48 -04:00
f77b63c078
compat: disable scroll-margin
This conflicts with the `smooth-scroll` Doom module.
2025-10-22 13:28:21 -04:00
296149638f
feat: enable new modules 2025-10-22 13:28:09 -04:00
4e2b5484b0
feat!: rewrite spell-checking section
This includes some prose rewriting, as well as an update to the code to
take advantage of a custom Aspell dictionary.
2025-10-22 13:27:24 -04:00
ad0b2cda72
tweak!: remove yuck-mode configuration
I don't use EWW anymore, so this serves no purpose for me.
2025-10-22 13:23:41 -04:00
b8b3fdea5e
tweak: modify org appearance 2025-10-22 13:21:08 -04:00
eb6b4b9b9c
fix: load autoloaded functions before patching 2025-10-22 13:19:47 -04:00
2 changed files with 148 additions and 92 deletions

View file

@ -67,7 +67,7 @@ The second block above was automatically generated by =org-mode=.
**** Tangle, Weave, Export, Publish
Since Org's ability to execute code and process its output is so robust, it's only natural that one might consider using Org to annotate a codebase, storing the code inside source blocks in the document. This would provide the space for much more thorough commentary than regular comments; however, it would prevent the file from being readable as a program, which is the entire point! You could maintain both the Org file and the actual source files separately, but that would require manually duplicating edits in both files, which is not ideal.
Since Org's ability to execute code and process its output is so robust, it's only natural that one might consider using Org to annotate a code-base, storing the code inside source blocks in the document. This would provide the space for much more thorough commentary than regular comments; however, it would prevent the file from being readable as a program, which is the entire point! You could maintain both the Org file and the actual source files separately, but that would require manually duplicating edits in both files, which is not ideal.
The solution is [[https://en.wikipedia.org/wiki/Literate_programming][literate programming]], the practice of embedding usable code into a markup document. Literate programming can be handled in a few different ways. A handful of languages support directly processing these markup documents, such as Haskell and Julia; however, the way Org implements it is closer to the traditional method, in which a singular file is processed in two different ways:
@ -84,7 +84,7 @@ The simultaneous handling of documentation and code inherent to literate program
Instead of documentation having to be bent around the restrictions of source code, the source code can be written and organized with all the freedoms of prose. If written well, the literate program can be structured in a manner closer to how the human mind understands code, rather than how a computer processes it. This is assisted by features such as macros, which provide further flexibility in structure.
It's not the right tool for every codebase, but proper use of literate programming can make a program much, much easier to comprehend and maintain. This is especially true for configuration languages like Emacs Lisp, where much of the code is conceptually disconnected and can easily be split apart.
It's not the right tool for every code-base, but proper use of literate programming can make a program much, much easier to comprehend and maintain. This is especially true for configuration languages like Emacs Lisp, where much of the code is conceptually disconnected and can easily be split apart.
** Current Issues
@ -808,7 +808,7 @@ The package allows you to define /patches/ which modify the definitions of funct
Some packages in this config such as =dirvish=, =org-roam=, etc. require certain tools to be in the environment. On a Nix-based system, there are a few different ways to handle this:
1. Put that tool in the actual environment, e.g. in a profile. This makes sense for simple things (=ripgrep=, =sqlite=, etc) but for more opinionated things like a specific version of Python it becomes less desirable.
1. Put that tool in the actual environment, e.g. in a profile. This makes sense for simple things (=ripgrep=, =sqlite=, etc) but for more opinionated things like a specific version of a compiler it becomes less desirable.
2. Build the tool and put a symlink to the output somewhere, e.g. in the HOME directory. This avoids polluting the environment, but you still have to deal with an unwieldy symlink that breaks Emacs if you accidentally delete it.
This was my approach before coming up with the third option:
3. Build the tool and point Emacs directly to the store path. This is the most automatic solution, but requires the most complex Emacs configuration.
@ -928,7 +928,7 @@ I'm a big fan of the Vertico ecosystem, as it's lightweight and easy to use. Let
The two most common syntax checking engines seem to be =flymake= and =flycheck=. =flymake= is built in to Emacs, is generally faster and currently has better support in the ecosystem, so let's use that one.
We'll also enable a dedicated spell checking module using ~aspell~, as that seems to be the recommended option.
In the past I used Doom Emacs's ~spell~ checker as well, but I eventually switched to a different system for spell-checking.
#+name: doom-checkers
#+begin_src emacs-lisp
@ -966,6 +966,7 @@ modeline
;;neotree
ophints
(popup +defaults)
smooth-scroll
;;tabs
;;(treemacs +lsp)
unicode
@ -990,6 +991,7 @@ fold
;;parinfer
;;rotate-text
snippets
(whitespace +guess +trim)
word-wrap
#+end_src
@ -999,7 +1001,7 @@ word-wrap
;;ansible
biblio
;;collab
;;debugger
debugger
direnv
;;docker
;;editorconfig
@ -1071,9 +1073,9 @@ Doom Emacs provides a large collection of modules for different languages. Which
#+name: doom-lang
#+begin_src emacs-lisp
:lang
(agda +tree-sitter +local)
(agda +tree-sitter)
;;beancount
;;(cc +lsp)
(cc +lsp +tree-sitter)
;;clojure
;;common-lisp
;;coq
@ -1111,14 +1113,14 @@ markdown
;;nim
(nix +lsp +tree-sitter)
;;ocaml
(org +roam2 +present
(org +roam +present
+gnuplot +jupyter
+pandoc +journal)
+pandoc +journal +pretty)
;;php
;;plantuml
;;purescript
(python +lsp +tree-sitter)
;;qt
(qt +lsp +tree-sitter)
;;racket
;;raku
;;rest
@ -1157,7 +1159,6 @@ It wouldn't be Emacs if there wasn't an endless list of config variables to chan
(executable-find "bash") ; Use bash instead of fish for default shell
disabled-command-function nil ; Disabled commands are a stupid idea
password-cache-expiry nil ; Security? Never heard of it
scroll-margin 2 ; A few extra lines on each end of the window
)
(global-subword-mode +1)
@ -1183,7 +1184,14 @@ The =auth-source-pass= package lets you use [[*Password Management][pass]] as a
#+begin_src emacs-lisp
(require 'auth-source-pass)
(setq auth-sources '(password-store "~/.authinfo.gpg")
auth-source-pass-filename
(or (getenv "PASSWORD_STORE_DIR")
(and (file-readable-p "~/.password-store") "~/.password-store")
"~/.local/share/password-store")
auth-source-cache-expiry nil)
;; Instantiate this so that child processes can inherit it
(setenv "PASSWORD_STORE_DIR" (expand-file-name auth-source-pass-filename))
#+end_src
** Bindings
@ -1286,7 +1294,7 @@ If PARENTS is non-nil, the parents of the specified directory will also be creat
*** Prefer Vertical Split
Emacs has a sophisticated system for controlling how windows and buffers are arranged on the screen, called ~display-buffer~. This works out of the box for almost every case I've thrown at it, except for one issue: it prefers horizontal splitting of windows to vertical splitting, despite the fact that the latter is far better in most cases.
Emacs has a sophisticated system for controlling how windows and buffers are arranged on the screen, called ~display-buffer~. This works out of the box for almost every case I've thrown at it, except for one issue: it prefers horizontal splitting of windows to vertical splitting (side-by-side), despite the fact that the latter is far better in most circumstances.
#+begin_src emacs-lisp
(defpatch! nil
@ -1384,7 +1392,7 @@ If you're going to be staring at your screen for hours a day, you might as well
** Theme
My favorite color theme has always been Tokyo Night. I use it literally everywhere I can, and Emacs is no exception.
My favorite color theme has always been [[https://marketplace.visualstudio.com/items?itemName=enkia.tokyo-night][Tokyo Night]]. I use it literally everywhere I can, and Emacs is no exception.
#+begin_src emacs-lisp
(setq doom-theme 'doom-tokyo-night)
@ -2123,6 +2131,13 @@ Forge is a convenient package for working with remote code forges like GitHub, G
forge-alist))
#+end_src
#+begin_src emacs-lisp :tangle packages.el
(package! glab)
(package! gtea)
(package! gogs)
(package! buck)
#+end_src
** Marginalia
#+call: confpkg("Pkg: marginalia")
@ -2239,6 +2254,9 @@ When editing a snippet, the binding =C-c C-t= can be used to test it in a fresh
Doom's command to create a new snippet, ~+snippets/new~, defines a template inside of itself purely for when creating a snippet through this command. This doesn't make much sense to me when file templates already exist as a standard system in Doom, and snippets are stored inside files!
#+begin_src emacs-lisp
;; HACK: Force snippet function to load
(autoload-do-load #'+snippets/new)
(defpatch! nil
(defun +snippets/new) (&optional all-modes)
"Create a new snippet in `+snippets-dir'.
@ -2275,6 +2293,9 @@ Doom's =file-templates= module extends =yasnippet= to provide a nice file templa
This system works well for the most part, but there's a serious issue with its UI: the function that registers file templates, ~set-file-templates!~, takes a plist to configure the template. If this list is empty, an existing template is removed instead. This is not only unintuitive, but it prevents you from having an empty property list, which is often necessary! We'll patch the function to remove this issue with a =:remove= key, as well as to have templates appended to the alist instead of prepended to make the order of templates more clear.
#+begin_src emacs-lisp
;; HACK: Force templates function to load
(autoload-do-load #'set-file-template!)
(defpatch! nil
(defun +file-templates--set) (pred plist)
(if (el-patch-swap (null (car-safe plist))
@ -2292,12 +2313,18 @@ Now that we have our new-and-improved template registry system, we can add new f
**** Nix
The default Nix file template is for a NixOS module. This is nice when you're writing a module, but most Nix files aren't modules. We'll restrict the default template to only trigger for files inside ~/etc/nixos~, and for there to be no template in other cases. We'll also add a template for flakes.
The default Nix file template is for a NixOS module. This is nice when you're writing a module, but most Nix files aren't modules. We'll restrict the default template to only trigger for files inside NixOS configurations, and for there to be no template in other cases. We'll also add a template for flakes.
#+begin_src emacs-lisp
(defvar ~/nixos-config-projects '()
"A list of project directories which contain NixOS configurations.")
(setq ~/nixos-config-projects
(mapcar #'expand-file-name
'("/etc/nixos/" "~/nix/nixos-config/" "~/nix/aether/")))
(set-file-templates!
'(nix-mode :remove t)
'("/etc/nixos/.*\\.nix$" :mode nix-mode)
'(nix-mode :when (lambda (_) (member (doom-project-root) ~/nixos-config-projects)))
'("/flake\\.nix$" :trigger "__flake.nix" :mode nix-mode))
#+end_src
@ -2305,33 +2332,58 @@ The default Nix file template is for a NixOS module. This is nice when you're wr
#+call: confpkg("Pkg: ispell")
Doom Emacs sets up spell-checking with ~ispell~ (Emacs-internal tool) and =aspell= (external tool) for us, which is nice. We just need to set which dictionary to use:
Keeping with its general theme of flexibility at the cost of pain, Emacs's spell-checking functionality requires several different packages to be installed, as well as an external spell-checking program to interface with. Let's set all these up in order:
#+begin_src emacs-lisp
(after! ispell
(setq ispell-dictionary "en_US"))
*** Aspell
The recommended external spell-checker is GNU Aspell, which takes a dictionary of words in a particular language and uses it to spell-check a text file. The default English dictionary is named =en_US=; I've created my own dictionary file based on it named =en_US-custom= with Aspell's multi-dictionary feature.
#+begin_src sh
# en_US-custom.multi
add en_US-w_accents.multi
add en-computers.rws
add en_US-science.rws
#+end_src
We also need to generate a plain-text dictionary for some ~ispell~ functionality, which is annoying, but I haven't figured out a way around it. I could use my automated nix-build system for this, but in this case it's easier to just put it in the usual location.
*** Ispell
Now that we have our new-and-improved template registry system, we can add new file templates as we please.
The most basic spell-checker package in Emacs is the built-in ~ispell~. This package doesn't actually provide any of the standard front-end of a spell-checker you would expect from a modern text editor, it just manages Aspell. It's a good start, though.
First, we need to give it our custom dictionary:
#+begin_src emacs-lisp
(defvar ~/plaintext-dict (expand-file-name "ispell/dict.plain" doom-data-dir)
"File location of a plaintext wordlist for spellchecking.")
(setq ispell-dictionary "en_US-custom")
#+end_src
We then also need a plain-text dictionary for certain features, like spelling correction. This is annoying, but easy to implement with some light scripting.
#+begin_src emacs-lisp
(defvar ~/plaintext-dict
(expand-file-name (concat "ispell/" ispell-dictionary ".txt")
doom-data-dir)
"File location of a plain-text wordlist for spell-checking.")
(unless (file-readable-p ~/plaintext-dict)
(shell-command-to-string
(concat
"aspell -l en_US dump master > " ~/plaintext-dict ";"
"aspell -d en-computers.rws dump master >> " ~/plaintext-dict ";"
"aspell -d en_US-science.rws dump master >> " ~/plaintext-dict ";")))
(concat "aspell -d '" ispell-dictionary "' dump master > " ~/plaintext-dict)))
(after! ispell
(setq ispell-alternate-dictionary ~/plaintext-dict))
(setq ispell-alternate-dictionary ~/plaintext-dict)
#+end_src
Now that we have this word list, we can also plug it into ~cape-dict~ and get proper spelling completion!
Ispell also manages our personal dictionary, which it places in a reasonable default position.
#+begin_src emacs-lisp
;; Don't ask for confirmation to save to personal dictionary
(setq ispell-silently-savep t)
#+end_src
*** ~spell-fu~
In order to modernize our spell-checker, we need the ubiquitous red squiggly lines to indicate incorrectly spelled words. The package ~spell-fu~ is serviceable for this purpose.
*** ~cape~
We now have spelling correction working, but no spelling completion. The package ~cape~ offers in-buffer completion for various things, including for dictionary words. We just need to provide it the plain-text dictionary we created earlier.
#+begin_src emacs-lisp
(after! cape
@ -2371,7 +2423,6 @@ Emacs comes with a few standard commands for selecting different windows. These
There's just one wrinkle: Doom Emacs's popup management system. Popups are treated as separate from "real" windows, usually displayed on the edge of the frame and without a modeline. =winum= has no knowledge of popup windows and assigns them numbers exactly the same as any other window. This can get really confusing, since windows get renumbered every time a new popup appears.
To solve this issue, I've written my own fork of =winum=:
#+begin_src emacs-lisp :tangle packages.el
@ -2615,23 +2666,23 @@ Emacs Everywhere is a great idea. Unfortunately, the default package on MELPA us
emacs-everywhere-copy-command
'("sh" "-c" "wl-copy < %f")
emacs-everywhere-window-focus-command
'("hyprctl" "dispatch" "focuswindow" "address:%w")))
'("hyprctl" "dispatch" "focuswindow" "address:%w"))
;; Function for accessing current window
(defun emacs-everywhere--app-info-linux-hyprland ()
"Return information on the active window, on Hyprland."
(pcase-let*
((`(,window-id ,window-class ,window-title . ,window-dims-)
(split-string (shell-command-to-string
"hyprctl activewindow -j | jaq -r \
'.address, .class, .title, .at[], .size[]'")
"\n"))
(window-dims (mapcar #'string-to-number (butlast window-dims-))))
(make-emacs-everywhere-app
:id window-id
:class window-class
:title window-title
:geometry window-dims)))
;; Function for accessing current window
(defun emacs-everywhere--app-info-linux-hyprland ()
"Return information on the active window, on Hyprland."
(pcase-let*
((`(,window-id ,window-class ,window-title . ,window-dims-)
(split-string (shell-command-to-string
"hyprctl activewindow -j | jaq -r \
'.address, .class, .title, .at[], .size[]'")
"\n"))
(window-dims (mapcar #'string-to-number (butlast window-dims-))))
(make-emacs-everywhere-app
:id window-id
:class window-class
:title window-title
:geometry window-dims))))
(defpatch! emacs-everywhere
(defun emacs-everywhere--app-info-linux) ()
@ -2754,6 +2805,7 @@ Unfortunately, with that power comes a *lot* of configuration work up-front. It
org-footnote-auto-label 'confirm ; Allow editing of footnote names
;; Startup
org-startup-indented nil ; Turn off indentation
org-startup-with-inline-images t ; Do more stuff on startup
org-startup-with-latex-preview t
+org-startup-with-animated-gifs t
@ -2761,7 +2813,6 @@ Unfortunately, with that power comes a *lot* of configuration work up-front. It
;; Customize appearance
org-indent-indentation-per-level 0 ; No heading indentation
org-cycle-separator-lines 1 ; Keep 1-line padding between folded headings
org-hide-emphasis-markers t ; Hide *emphasis*
org-image-actual-width '(550) ; Default images to 550px
org-format-latex-options ; Make latex preview smaller
(plist-put org-format-latex-options :scale 0.55)
@ -2888,8 +2939,7 @@ It's sometimes nice to be able to click a link in an Org file that takes me to o
#+begin_src emacs-lisp
(after! org
(setq org-pretty-entities t
org-entities-user
(setq org-entities-user
'(("nbsp" "~" nil "&nbsp;" " " " " " ")
("top" "\\top" t "&top;" "" "22A4" "")
("bot" "\\bot" t "&perp;" "" "22A5" "⊥")
@ -2903,16 +2953,8 @@ It's sometimes nice to be able to click a link in an Org file that takes me to o
*** Modern
Doom Emacs's =org +pretty= flag by default uses the package =org-superstar= to prettify Org files. This package is decently nice looking, but it has a much nicer and more comprehensive alternative in the form of =org-modern=.
#+begin_src emacs-lisp :tangle packages.el
(package! org-modern)
#+end_src
#+begin_src emacs-lisp
(use-package! org-modern
:hook (org-mode . org-modern-mode)
:config
(after! org-modern
(setq org-modern-star 'replace
org-modern-replace-stars '("◉" "○" "✸" "✿" "✤" "✜" "◆" "▶")
org-modern-label-border -2
@ -2922,13 +2964,6 @@ Doom Emacs's =org +pretty= flag by default uses the package =org-superstar= to p
(?+ . "•")
(?* . "•"))
org-modern-todo nil
org-modern-todo-faces
'(("TODO" :inverse-video t :inherit org-todo)
("PROJ" :inverse-video t :inherit +org-todo-project)
("STRT" :inverse-video t :inherit +org-todo-active)
("WAIT" :inverse-video t :inherit +org-todo-onhold)
("HOLD" :inverse-video t :inherit +org-todo-onhold)
("KILL" :inverse-video t :inherit +org-todo-cancel))
org-modern-block-fringe nil
org-modern-block-name
'(("src" "»" "«")
@ -2977,9 +3012,13 @@ Doom Emacs's =org +pretty= flag by default uses the package =org-superstar= to p
("results" . " result")
(t . " "))
org-modern-checkbox
'((88 . "")
(45 . #("" 0 2 (composition ((2)))))
(32 . ""))))
'((?X . "")
(?- . #("" 0 2 (composition ((2)))))
(? . "")))
(remove-hook! 'org-modern-mode-hook
#'+org-modern-show-hidden-stars-in-indent-mode-h)
(remove-hook! 'org-agenda-finalize-hook
#'org-modern-agenda))
#+end_src
The default colors for various elements of =org-modern= don't match with our theme colors, so we need to modify them ourselves. We'll also modify a few aspects of label appearance here.
@ -3024,24 +3063,13 @@ The default colors for various elements of =org-modern= don't match with our the
*** Appear
Since we've disabled =+pretty=, we need to add the packages we do want from it, namely =org-appear= to automatically prettify emphasis markers and other nice things like that.
#+begin_src emacs-lisp :tangle packages.el
(package! org-appear)
#+end_src
#+begin_src emacs-lisp
(use-package! org-appear
:hook (org-mode . org-appear-mode)
:config
(after! org-appear
(setq org-appear-autoentities t
org-appear-autokeywords t
org-appear-autosubmarkers t
org-appear-autolinks 'just-brackets
org-appear-inside-latex t))
(after! org
(setq org-highlight-latex-and-related '(native script entities)))
#+end_src
*** Emphasis
@ -3583,7 +3611,6 @@ Since Org-roam is currently my primary method of using Org, I only use its templ
I want there to only be one capture template when I'm making new links, so that I don't get distracted by a prompt. However, when I'm explicitly telling Roam to use node capture, it would be a shame if there was only one option. To fix this, I can split my capture templates into three contexts: a default template for when not explicitly capturing, a list of templates for capturing on a node that already exists, and the regular list for when the node is new.
#+begin_src emacs-lisp
(defvar org-roam-capture-default-template nil
"The default capture template to use when not explicitly capturing.")
@ -3858,7 +3885,7 @@ Let's start with some configuration. I use [[https://www.zotero.org/][Zotero]] t
(setq org-cite-csl-styles-dir "~/Zotero/styles"
;; MLA style by default
org-cite-csl--fallback-style-file
"/home/kiana/Zotero/styles/modern-language-association.csl"
(expand-file-name "modern-language-association.csl" org-cite-csl-styles-dir)
org-cite-global-bibliography
(list (expand-file-name "library.json" org-directory))
citar-bibliography org-cite-global-bibliography))
@ -4060,7 +4087,7 @@ it defaults to `project-current'."
**** Window Numbering
If a side session is open, we'll configure =winum= to give it the index ~(popup . 0)~, i.e. the 0th popup window. It's not actually a popup, since the popup system's window management clashes with =dirvish='s, but it's convenient to treat it as one.
If a side session is open, we'll configure =winum= to give it the index ~(popup\nbsp{}.\nbsp{}0)~, i.e. the 0th popup window. It's not actually a popup, since the popup system's window management clashes with =dirvish='s, but it's convenient to treat it as one.
#+begin_src emacs-lisp
(after! (winum dirvish-side)
@ -4238,14 +4265,39 @@ Some more convenient leader key bindings would be nice to prevent having to traw
"c L" #'lsp-avy-lens)
#+end_src
** Yuck
** Markdown
#+call: confpkg("Mode: Yuck")
#+call: confpkg("Mode: Markdown")
Since I've started using [[https://github.com/elkowar/eww/tree/master?tab=readme-ov-file][EWW (Elkowar's Wacky Widgets)]] for my linux setup, it would be nice to have an editing mode for its configuration language.
*** Polymode
Markdown allows for inserting code from other languages into itself. To make this integration more seamless, we can use the =poly-markdown= package to add syntax highlighting and major mode features of the corresponding language to the buffer.
#+begin_src emacs-lisp :tangle packages.el
(package! yuck-mode)
(package! poly-markdown)
#+end_src
#+begin_src emacs-lisp
(add-hook! markdown-mode #'poly-markdown-mode)
#+end_src
**** Agda Polymode
The Agda integration with Markdown specifically requires some changes to the polymode, as its syntax highlighting scheme is different from the usual =font-lock= system that it expects.
#+begin_src emacs-lisp
(after! poly-markdown
(object-add-to-list
poly-markdown-fenced-code-innermode :init-functions
(defun ~/polymode-agda2-init-function (_)
(when (eq major-mode 'agda2-mode)
(font-lock-mode 0)
(setq indent-line-function #'indent-relative)))))
(after! agda2-mode
;; This highlighting interferes with Markdown's styling
(setq agda2-highlight-faces
(assq-delete-all 'background agda2-highlight-faces)))
#+end_src
** Slint

View file

@ -8,10 +8,14 @@
inputs = {
${2:nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
}${3:flake-utils.url = "github:numtide/flake-utils";
}$4
systems.url = "github:nix-systems/default";}
};
outputs = { $5 }:
$0
outputs = { self, ${3:nixpkgs, systems, }... }:
${3:$(when (and (string-match-p "nixpkgs[[:space:]]*," yas-text)
(string-match-p "systems[[:space:]]*," yas-text)) "let
eachSystem = nixpkgs.lib.genAttrs (import systems);
in ")}{
$0
};
}