Compare commits

..

No commits in common. "e2ae7b07361893d00e6fc0c72060dcb71e2b391c" and "7f7050d079e5c9220d871e9640f03b7321d4f5d8" have entirely different histories.

2 changed files with 436 additions and 132 deletions

View file

@ -810,7 +810,7 @@ I'm a big fan of the Vertico ecosystem, as it's lightweight and easy to use. Let
#+begin_src emacs-lisp #+begin_src emacs-lisp
:completion :completion
(vertico +icons) (vertico +icons)
(corfu +icons +orderless) (company +childframe)
#+end_src #+end_src
** Checkers ** Checkers
@ -898,7 +898,7 @@ direnv
;;gist ;;gist
(lookup +docsets) (lookup +docsets)
lsp lsp
(magit +forge) magit
make make
pass pass
pdf pdf
@ -1441,16 +1441,81 @@ Our ~package!~ declarations go in ~packages.el~, which must not be byte-compiled
Everything else goes in ~config.el~, which is managed by [[*=confpkg=][confpkg]] as outlined earlier. Everything else goes in ~config.el~, which is managed by [[*=confpkg=][confpkg]] as outlined earlier.
** Corfu ** Company
#+call: confpkg("Pkg: corfu") #+call: confpkg("Pkg: company")
*** Bindings
When Company is active, its keybindings overshadow the default ones, meaning keys like =RET= no longer work. To prevent this from happening, let's rebind ~company-complete-selection~ to =TAB= (less useful in the middle of typing), and only allow =RET= to be used if Company has been explicitly interacted with.
#+begin_src emacs-lisp #+begin_src emacs-lisp
(after! corfu (let ((item
;; I don't really use TAB very often, so prefer other actions `(menu-item nil company-complete-selection
(setq +corfu-want-tab-prefer-navigating-snippets t :filter ,(lambda (cmd)
+corfu-want-tab-prefer-expand-snippets t (when (company-explicit-action-p)
+corfu-want-tab-prefer-navigating-org-tables t)) cmd)))))
(map! :after company
:map company-active-map
"RET" item
"<return>" item
"TAB" #'company-complete-selection
"<tab>" #'company-complete-selection
"S-TAB" #'company-complete-common))
#+end_src
*** Spell Correction
#+call: confpkg("Pkg: company-spell")
I've been having problems with ~company-ispell~, mainly due to Ispell requiring a text-based dictionary (unlike Aspell, which uses a binary dictionary). So let's switch to ~company-spell~:
#+begin_src emacs-lisp :tangle packages.el
(package! company-spell)
#+end_src
#+begin_src emacs-lisp
(after! company-spell
(map! :map evil-insert-state-map
"C-x s" #'company-spell))
#+end_src
We should make sure that ~company-spell~ uses Ispell's personal dictionary too:
#+begin_src emacs-lisp
(after! (company-spell ispell)
(setq company-spell-args
(concat company-spell-args " -p " ispell-personal-dictionary)))
#+end_src
*** Icons
The ~company-box~ front-end adds support for icons, but there aren't many providers for them, especially in text. We'll add two new icon providers:
- ~~/company-box-icons--text~, which directly targets the output of ~company-spell~
- ~~/company-box-icons--spell~, which is a fallback for all text completions
#+begin_src emacs-lisp
;; Mark candidates from `company-spell' using a text property
(defadvice! ~/company-spell-text-property (words)
:filter-return #'company-spell-lookup-words
(dolist (word words)
(put-text-property 0 1 'spell-completion-item t word))
words)
(defun ~/company-box-icons--spell (candidate)
(when (get-text-property 0 'spell-completion-item candidate)
'Text))
(defun ~/company-box-icons--text (candidate)
(when (derived-mode-p 'text-mode) 'Text))
(after! company-box
(pushnew! company-box-icons-functions #'~/company-box-icons--text)
;; `~/company-box-icons--text' is a fallback, so it has to go at the end of
;; the list
(setq company-box-icons-functions
(append company-box-icons-functions '(~/company-box-icons--text))))
#+end_src #+end_src
** Eldoc ** Eldoc
@ -1961,6 +2026,39 @@ Having an IDE-style tooltip pop up is nice, but ~flymake-popon~ is a bit ugly by
(set-popup-rule! "^\\*Flymake" :vslot 1 :side 'bottom)) (set-popup-rule! "^\\*Flymake" :vslot 1 :side 'bottom))
#+end_src #+end_src
** Indent Guides
#+call: confpkg("Pkg: highlight-indent-guides")
I've found that character-based indent guides work best.
#+begin_src emacs-lisp
(after! highlight-indent-guides
(setq highlight-indent-guides-method 'character
highlight-indent-guides-character 9615
highlight-indent-guides-responsive 'top
highlight-indent-guides-auto-character-face-perc 90
highlight-indent-guides-auto-top-character-face-perc 200))
#+end_src
** Language Servers
#+call: confpkg("Pkg: lsp")
~lsp-mode~ requires ~avy~, but doesn't load it for some reason.
#+begin_src emacs-lisp
;; (advice-add #'lsp-avy-lens :before (cmd! (require 'avy)))
#+end_src
Here's a convenient leader key binding as well:
#+begin_src emacs-lisp
(map! :leader
:desc "Select LSP code lens"
"c L" #'lsp-avy-lens)
#+end_src
** Git ** Git
#+call: confpkg("Pkg: magit") #+call: confpkg("Pkg: magit")
@ -1985,33 +2083,6 @@ Magit already looks great, but it could use some proper syntax highlighting!
:hook (magit-mode . magit-delta-mode)) :hook (magit-mode . magit-delta-mode))
#+end_src #+end_src
** Indent Guides
#+call: confpkg("Pkg: highlight-indent-guides")
I've found that character-based indent guides work best.
#+begin_src emacs-lisp
(after! highlight-indent-guides
(setq highlight-indent-guides-method 'character
highlight-indent-guides-character 9615
highlight-indent-guides-responsive 'top
highlight-indent-guides-auto-character-face-perc 90
highlight-indent-guides-auto-top-character-face-perc 200))
#+end_src
** Language Servers
#+call: confpkg("Pkg: lsp")
Some more convenient leader key bindings would be nice to prevent having to trawl through the =lsp-command-map=.
#+begin_src emacs-lisp
(map! :leader
:desc "Select LSP code lens"
"c L" #'lsp-avy-lens)
#+end_src
** Marginalia ** Marginalia
#+call: confpkg("Pkg: marginalia") #+call: confpkg("Pkg: marginalia")
@ -2091,8 +2162,6 @@ I like having ophints for vim editing so that I don't get lost when making large
Snippets are a sophisticated method of warding off the scourge of unnecessary keystrokes. They were a bit hard to get used to, but I've warmed up to them over time. Snippets are a sophisticated method of warding off the scourge of unnecessary keystrokes. They were a bit hard to get used to, but I've warmed up to them over time.
The snippets themselves are stored in separate files, each of which can be found in the =snippets/= subdirectory of this repo. As the ~yasnippet~ package expects, they are grouped by major mode.
*** Tweaks *** Tweaks
Allow nested snippets: Allow nested snippets:
@ -2142,44 +2211,6 @@ Doom's command to create a new snippet, ~+snippets/new~, defines a template insi
(find-file snippet-file-name)))) (find-file snippet-file-name))))
#+end_src #+end_src
** Spell Checking
#+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:
#+begin_src emacs-lisp
(after! ispell
(setq ispell-dictionary "en_US"))
#+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 I want to have access to my =aspell= config file, so it's easier to just put it in the usual location.
#+begin_src emacs-lisp
(defvar ~/plaintext-dict (expand-file-name "ispell/dict.plain" doom-data-dir)
"File location of a plaintext wordlist for spellchecking.")
(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 ";")))
(after! ispell
(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!
#+begin_src emacs-lisp
(after! cape
(setq cape-dict-file ~/plaintext-dict))
(add-hook! text-mode
(add-hook! 'completion-at-point-functions :local :depth 40 #'cape-dict))
#+end_src
** Treemacs ** Treemacs
#+call: confpkg("Pkg: treemacs") #+call: confpkg("Pkg: treemacs")
@ -2561,9 +2592,9 @@ four capitalized letters), and the cdr is a list of sub-tags.")
"Classes that belong under the :Online: tag.") "Classes that belong under the :Online: tag.")
(after! org (after! org
(setq classes-mwf '() (setq classes-mwf '(("HIST1111" . ?1))
classes-tr '() classes-tr '(("MATH2203" . ?2))
classes-online '() classes-online '(("HIST2111" . ?3))
subject-alist '(("MATH" ("calculus") ("algebra")) subject-alist '(("MATH" ("calculus") ("algebra"))
("HIST") ("HIST")
("POLS") ("POLS")
@ -2682,8 +2713,7 @@ Doom Emacs's =+pretty= flag by default uses the package =org-superstar= to prett
(use-package! org-modern (use-package! org-modern
:hook (org-mode . org-modern-mode) :hook (org-mode . org-modern-mode)
:config :config
(setq org-modern-star 'replace (setq org-modern-star '("◉" "○" "✸" "✿" "✤" "✜" "◆" "▶")
org-modern-replace-stars '("◉" "○" "✸" "✿" "✤" "✜" "◆" "▶")
org-modern-label-border 0.3 org-modern-label-border 0.3
org-modern-table-vertical 1 org-modern-table-vertical 1
org-modern-table-horizontal 0.2 org-modern-table-horizontal 0.2
@ -2784,12 +2814,9 @@ The default colors for various elements of =org-modern= don't match with our the
:foreground ,(doom-color 'grey) :foreground ,(doom-color 'grey)
:background ,(doom-color 'modeline-bg-l) :background ,(doom-color 'modeline-bg-l)
:inherit org-modern-time-active) :inherit org-modern-time-active)
`(org-modern-progress-incomplete `(org-modern-statistics
:background ,(doom-color 'bg-alt) :foreground ,(doom-color 'yellow)
:foreground ,(doom-color 'fg-alt)) :inherit org-checkbox-statistics-todo)))
`(org-modern-progress-complete
:background ,(doom-color 'yellow)
:foreground ,(doom-color 'bg-alt))))
#+end_src #+end_src
*** Appear *** Appear
@ -2820,10 +2847,6 @@ When marking text for =*emphasis*=, Org mode normally only allows emphasized sec
(setf (nth 4 org-emphasis-regexp-components) 20)) (setf (nth 4 org-emphasis-regexp-components) 20))
#+end_src #+end_src
*** Popups
Org uses many popup windows for various things, and we can use Doom to make them look a little nicer.
** Enhancements ** Enhancements
While Org-mode provides a very comprehensive set of tools and systems, there are a few small things that are missing or that would make the overall UX smoother. Luckily, Org-mode being implemented as an Emacs package gives us more than enough control over its function to crowbar in a few new features! While Org-mode provides a very comprehensive set of tools and systems, there are a few small things that are missing or that would make the overall UX smoother. Luckily, Org-mode being implemented as an Emacs package gives us more than enough control over its function to crowbar in a few new features!
@ -3073,6 +3096,316 @@ To fix this, we'll need some liberal use of override advising.
(cl-incf alt))) (cl-incf alt)))
(push (cons (car e) (or (car clist) alt)) new)))) (push (cons (car e) (or (car clist) alt)) new))))
(nreverse new))) (nreverse new)))
(defadvice! ~/org-fast-tag-selection (current-tags inherited-tags tag-table &optional todo-table)
:override #'org-fast-tag-selection
(let* (;; Combined alist of all the tags and todo keywords.
(tag-alist (append tag-table todo-table))
;; Max width occupied by a single tag record in the completion buffer.
(field-width
(+ 3 ; keep space for "[c]" binding.
1 ; ensure that there is at least one space between adjacent tag fields.
3 ; keep space for group tag " : " delimiter.
;; The longest tag.
(if (null tag-alist) 0
(apply #'max
(mapcar (lambda (x)
(if (stringp (car x)) (string-width (car x))
0))
tag-alist)))))
(origin-buffer (current-buffer))
(expert-interface (eq org-fast-tag-selection-single-key 'expert))
;; Tag completion table, for normal completion (<TAB>).
(tab-tags nil)
(inherited-face 'org-done)
(current-face 'org-todo)
;; Characters available for auto-assignment.
(tag-binding-char-list org--fast-tag-selection-keys)
(tag-binding-chars-left org-fast-tag-selection-maximum-tags)
field-number ; current tag column in the completion buffer.
tag-binding-spec ; Alist element.
current-tag current-tag-char auto-tag-char
tag-table-local ; table holding all the displayed tags together with auto-assigned bindings.
input-char rtn
ov-start ov-end ov-prefix
(exit-after-next org-fast-tag-selection-single-key)
(done-keywords org-done-keywords)
groups ingroup intaggroup char-tags)
;; Calculate the number of tags with explicit user bindings + tags in groups.
;; These tags will be displayed unconditionally. Other tags will
;; be displayed only when there are free bindings left according
;; to `org-fast-tag-selection-maximum-tags'.
(dolist (tag-binding-spec tag-alist)
(pcase tag-binding-spec
(`((or :startgroup :startgrouptag) . _)
(setq ingroup t))
(`((or :endgroup :endgrouptag) . _)
(setq ingroup nil))
((guard (cdr tag-binding-spec))
(cl-decf tag-binding-chars-left))
(`((or :newline :grouptags))) ; pass
((guard ingroup)
(cl-decf tag-binding-chars-left))))
(setq ingroup nil) ; It t, it means malformed tag alist. Reset just in case.
;; Move global `org-tags-overlay' overlay to current heading.
;; Calls to `org-set-current-tags-overlay' will take care about
;; updating the overlay text.
;; FIXME: What if we are setting file tags?
(save-excursion
(forward-line 0)
(if (looking-at org-tag-line-re)
(setq ov-start (match-beginning 1)
ov-end (match-end 1)
ov-prefix "")
(setq ov-start (1- (line-end-position))
ov-end (1+ ov-start))
(skip-chars-forward "^\n\r")
(setq ov-prefix
(concat
(buffer-substring (1- (point)) (point))
(if (> (current-column) org-tags-column)
" "
(make-string (- org-tags-column (current-column)) ?\ ))))))
(move-overlay org-tags-overlay ov-start ov-end)
;; Highlight tags overlay in Org buffer.
(org-set-current-tags-overlay current-tags ov-prefix)
;; Display tag selection dialogue, read the user input, and return.
(save-excursion
(save-window-excursion
;; Select tag list buffer, and display it unless EXPERT-INTERFACE.
(if expert-interface
(set-buffer (get-buffer-create " *Org tags*"))
(delete-other-windows)
(set-window-buffer (split-window-vertically) (get-buffer-create " *Org tags*"))
(switch-to-buffer-other-window " *Org tags*"))
;; Fill text in *Org tags* buffer.
(erase-buffer)
(setq-local org-done-keywords done-keywords)
;; Insert current tags.
(org-fast-tag-insert "Inherited" inherited-tags inherited-face "\n")
(org-fast-tag-insert "Current" current-tags current-face "\n\n")
;; Display whether next change exits selection dialogue.
(org-fast-tag-show-exit exit-after-next)
;; Show tags, tag groups, and bindings in a grid.
;; Each tag in the grid occupies FIELD-WIDTH characters.
;; The tags are filled up to `window-width'.
(setq field-number 0)
(while (setq tag-binding-spec (pop tag-alist))
(pcase tag-binding-spec
;; Display tag groups on starting from a new line.
(`(:startgroup . ,group-name)
(push '() groups) (setq ingroup t)
(unless (zerop field-number)
(setq field-number 0)
(insert "\n"))
(insert (if group-name (format "%s: " group-name) "") "{ "))
;; Tag group end is followed by newline.
(`(:endgroup . ,group-name)
(setq ingroup nil field-number 0)
(insert "}" (if group-name (format " (%s) " group-name) "") "\n"))
;; Group tags start at newline.
(`(:startgrouptag)
(setq intaggroup t)
(unless (zerop field-number)
(setq field-number 0)
(insert "\n"))
(insert "[ "))
;; Group tags end with a newline.
(`(:endgrouptag)
(setq intaggroup nil field-number 0)
(insert "]\n"))
(`(:newline)
(unless (zerop field-number)
(setq field-number 0)
(insert "\n")
(setq tag-binding-spec (car tag-alist))
(while (equal (car tag-alist) '(:newline))
(insert "\n")
(setq tag-alist (cdr tag-alist)))))
(`(:grouptags)
;; Previous tag is the tag representing the following group.
;; It was inserted as "[c] TAG " with spaces filling up
;; to the field width. Replace the trailing spaces with
;; " : ", keeping to total field width unchanged.
(delete-char -3)
(insert " : "))
(_
(setq current-tag (copy-sequence (car tag-binding-spec))) ; will be modified by side effect
(if (or (member current-tag char-tags)
(and (string-prefix-p "{" current-tag)
(string-suffix-p "}" current-tag)))
(setq current-tag-char nil)
;; Compute tag binding.
(if (cdr tag-binding-spec)
;; Custom binding.
(setq current-tag-char (cdr tag-binding-spec))
;; No auto-binding. Update `tag-binding-chars-left'.
(unless (or ingroup intaggroup) ; groups are always displayed.
(cl-decf tag-binding-chars-left))
;; Automatically assign a character according to the tag string.
(setq auto-tag-char
(string-to-char
(downcase (substring
current-tag (if (= (string-to-char current-tag) ?@) 1 0)))))
(if (or (rassoc auto-tag-char tag-table-local)
(rassoc auto-tag-char tag-table))
;; Already bound. Assign first unbound char instead.
(progn
(while (and tag-binding-char-list
(or (rassoc (car tag-binding-char-list) tag-table-local)
(rassoc (car tag-binding-char-list) tag-table)))
(pop tag-binding-char-list))
(setq current-tag-char (or (car tag-binding-char-list)
;; Fall back to display "[ ]".
?\s)))
;; Can safely use binding derived from the tag string.
(setq current-tag-char auto-tag-char)))
(push current-tag char-tags))
;; Record all the tags in the group. `:startgroup'
;; clause earlier added '() to `groups'.
;; `(car groups)' now contains the tag list for the
;; current group.
(when ingroup (push current-tag (car groups)))
;; Compute tag face.
(setq current-tag (org-add-props current-tag nil 'face
(cond
((not (assoc current-tag tag-table))
;; The tag is from TODO-TABLE.
(org-get-todo-face current-tag))
((member current-tag current-tags) current-face)
((member current-tag inherited-tags) inherited-face))))
(when (equal (caar tag-alist) :grouptags)
(org-add-props current-tag nil 'face 'org-tag-group))
;; Respect `org-fast-tag-selection-maximum-tags'.
(when (or ingroup intaggroup (cdr tag-binding-spec) (> tag-binding-chars-left 0))
;; Insert the tag.
(when (and (zerop field-number) (not ingroup) (not intaggroup)) (insert " "))
(if current-tag-char
(insert "[" current-tag-char "] ")
(insert " "))
(insert current-tag
;; Fill spaces up to FIELD-WIDTH.
(make-string
(- field-width 4 (length current-tag)) ?\ ))
;; Record tag and the binding/auto-binding.
(push (cons current-tag current-tag-char) tag-table-local)
;; Last column in the row.
(when (= (cl-incf field-number) (/ (- (window-width) 4) field-width))
(unless (memq (caar tag-alist) '(:endgroup :endgrouptag))
(insert "\n")
(when (or ingroup intaggroup) (insert " ")))
(setq field-number 0))))))
(insert "\n")
;; Keep the tags in order displayed. Will be used later for sorting.
(setq tag-table-local (nreverse tag-table-local))
(goto-char (point-min))
(unless expert-interface (org-fit-window-to-buffer))
;; Read user input.
(setq rtn
(catch 'exit
(while t
(message "[a-z..]:toggle [SPC]:clear [RET]:accept [TAB]:edit [!] %sgroups%s"
(if (not groups) "no " "")
(if expert-interface " [C-c]:window" (if exit-after-next " [C-c]:single" " [C-c]:multi")))
(setq input-char
(let ((inhibit-quit t)) ; intercept C-g.
(read-char-exclusive)))
;; FIXME: Global variable used by `org-beamer-select-environment'.
;; Should factor it out.
(setq org-last-tag-selection-key input-char)
(pcase input-char
;; <RET>
(?\r (throw 'exit t))
;; Toggle tag groups.
(?!
(setq groups (not groups))
(goto-char (point-min))
(while (re-search-forward "[{}]" nil t) (replace-match " ")))
;; Toggle expert interface.
(?\C-c
(if (not expert-interface)
(org-fast-tag-show-exit
(setq exit-after-next (not exit-after-next)))
(setq expert-interface nil)
(delete-other-windows)
(set-window-buffer (split-window-vertically) " *Org tags*")
(switch-to-buffer-other-window " *Org tags*")
(org-fit-window-to-buffer)))
;; Quit.
((or ?\C-g ?q)
(delete-overlay org-tags-overlay)
;; Quit as C-g does.
(keyboard-quit))
;; Clear tags.
(?\s
(setq current-tags nil)
(when exit-after-next (setq exit-after-next 'now)))
;; Use normal completion.
(?\t
;; Compute completion table, unless already computed.
(unless tab-tags
(setq tab-tags
(delq nil
(mapcar (lambda (x)
(let ((item (car-safe x)))
(and (stringp item)
(list item))))
;; Complete using all tags; tags from current buffer first.
(org--tag-add-to-alist
(with-current-buffer origin-buffer
(org-get-buffer-tags))
tag-table)))))
(setq current-tag (completing-read "Tag: " tab-tags))
(when (string-match "\\S-" current-tag)
(cl-pushnew (list current-tag) tab-tags :test #'equal)
(setq current-tags (org--add-or-remove-tag current-tag current-tags groups)))
(when exit-after-next (setq exit-after-next 'now)))
;; INPUT-CHAR is for a todo keyword.
((let (and todo-keyword (guard todo-keyword))
(car (rassoc input-char todo-table)))
(with-current-buffer origin-buffer
(save-excursion (org-todo todo-keyword)))
(when exit-after-next (setq exit-after-next 'now)))
;; INPUT-CHAR is for a tag.
((let (and tag (guard tag))
(car (rassoc input-char tag-table-local)))
(setq current-tags (org--add-or-remove-tag tag current-tags groups))
(when exit-after-next (setq exit-after-next 'now))))
;; Create a sorted tag list.
(setq current-tags
(sort current-tags
(lambda (a b)
;; b is after a.
;; `memq' returns tail of the list after the match + the match.
(assoc b (cdr (memq (assoc a tag-table-local) tag-table-local))))))
;; Exit when we are set to exit immediately.
(when (eq exit-after-next 'now) (throw 'exit t))
;; Continue setting tags in the loop.
;; Update the currently active tags indication in the completion buffer.
(goto-char (point-min))
(forward-line 1)
(delete-region (point) (line-end-position))
(org-fast-tag-insert "Current" current-tags current-face)
;; Update the active tags displayed in the overlay in Org buffer.
(org-set-current-tags-overlay current-tags ov-prefix)
;; Update tag faces in the displayed tag grid.
(let ((tag-re (concat "\\[.\\] \\(" org-tag-re "\\)")))
(while (re-search-forward tag-re nil t)
(let ((tag (match-string 1)))
(add-text-properties
(match-beginning 1) (match-end 1)
(list 'face
(cond
((member tag current-tags) current-face)
((member tag inherited-tags) inherited-face)
(t 'default)))))))
(goto-char (point-min)))))
;; Clear the tag overlay in Org buffer.
(delete-overlay org-tags-overlay)
;; Return the new tag list.
(if rtn
(mapconcat 'identity current-tags ":")
nil)))))
#+end_src #+end_src
The last problem is that Org automatically assigns keys alphabetically if not specified, which means keys can often be difficult to reach. To fix this, we can simply configure some variables. The last problem is that Org automatically assigns keys alphabetically if not specified, which means keys can often be difficult to reach. To fix this, we can simply configure some variables.
@ -3282,18 +3615,18 @@ Annoyingly, the only good way to fix these issues is to completely override the
When an explicit category is not specified, Org mode typically defaults to the filename (sans extension). This ... sort of makes sense? I guess? It doesn't really, because filename conventions don't make for good agenda category names. I want my category names to be in title case, whereas a file name is typically going to be all lowercase and without spaces. This is especially bad for Org-roam, where filenames are automatically generated and way too long to be a UI element. When an explicit category is not specified, Org mode typically defaults to the filename (sans extension). This ... sort of makes sense? I guess? It doesn't really, because filename conventions don't make for good agenda category names. I want my category names to be in title case, whereas a file name is typically going to be all lowercase and without spaces. This is especially bad for Org-roam, where filenames are automatically generated and way too long to be a UI element.
To fix this issue, it's... "easy" to patch Org-mode's category system[fn:2]. The following code sets things up so that the file's =#+title= metadata is used as the default category, falling back on the default behavior if a title is not given. To fix this issue, it is thankfully rather simple to patch Org-mode's category system[fn:2]. The following code sets things up so that the file's =#+title= metadata is used as the default category, falling back on the default behavior if a title is not given.
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defadvice! ~/org-default-category (old-fn) (defadvice! ~/org-default-category (old-fn)
"Modify how Org resolves the default category through the "Modify how Org resolves the default category through the
`org-category' variable." `org-category' variable."
:around '(org-refresh-category-properties org-element-org-data-parser) :around '(org-refresh-category-properties org-element--get-category)
(let ((org-category (or org-category (org-get-title)))) (let ((org-category (or org-category (org-get-title))))
(funcall old-fn))) (funcall old-fn)))
#+end_src #+end_src
[fn:2] It took me multiple hours of combing through Org's source code in order to find the multiple places(???) where this behavior was implemented and to figure out how to modify it. At least the final code is short! [fn:2] Where by "simple" I mean that it took me multiple hours of combing through Org's source code in order to find the multiple places(???) where this behavior was implemented and to figure out how to modify it. At least the final code is short!
** Org Roam ** Org Roam
@ -3353,14 +3686,7 @@ By default, Roam generates file names containing both a timestamp and the node t
*** Roam Buffer *** Roam Buffer
The unlinked references section is turned off by default for performance reasons, but sometimes I want to see that information when writing notes. I also don't want it to show up for every node, because some of my nodes have quite a large reference list. Let's add a predicate to control when unlinked references are shown. The unlinked references section is turned off by default for performance reasons, but I've never had any serious issues with it. Let's turn that on, and also make sure backlinks are unique.
#+begin_src emacs-lisp
(defadvice! ~/org-roam-unlinked-references-p (node)
"A predicate to control when unlinked references are shown in the `org-roam' buffer."
:before-while #'org-roam-unlinked-references-section
(assoc "UNLINKED_REFS" (org-roam-node-properties node)))
#+end_src
#+begin_src emacs-lisp #+begin_src emacs-lisp
(after! org-roam (after! org-roam
@ -3536,9 +3862,9 @@ A full week-long agenda is usually too cluttered for me to read, so I'll narrow
org-agenda-start-on-weekday 1 ; 1 = Monday org-agenda-start-on-weekday 1 ; 1 = Monday
org-agenda-sorting-strategy org-agenda-sorting-strategy
'((agenda time-up habit-down prority-down category-up) '((agenda time-up habit-down urgency-down category-up)
(todo habit-down priority-down time-up category-up) (todo habit-down urgency-down time-up category-up)
(tags habit-down priority-down time-up category-up) (tags habit-down urgency-down time-up category-up)
(search category-up)) (search category-up))
;; Make sure agenda is the only window ;; Make sure agenda is the only window
@ -3762,17 +4088,6 @@ To make opening the journal more convenient, here's a command to open the latest
Despite Emacs being my editor of choice for programming, I don't actually have a lot of configuration for programming languages. I suppose that this is because language packages tend to not need much personal configuration, as the bounds of what a language mode needs to do are typically defined by the language itself. Despite Emacs being my editor of choice for programming, I don't actually have a lot of configuration for programming languages. I suppose that this is because language packages tend to not need much personal configuration, as the bounds of what a language mode needs to do are typically defined by the language itself.
** Indentation
#+call: confpkg("Indent")
I prefer 2-space indentation in all circumstances. Unfortunately, Emacs's indentation is mode-specific, so I have to configure every mode's indentation separately.
#+begin_src emacs-lisp
(after! css-mode
(setq css-indent-offset 2))
#+end_src
** Dired ** Dired
#+call: confpkg("Mode: Dired") #+call: confpkg("Mode: Dired")
@ -3823,9 +4138,8 @@ The Doom module is very outdated, so I'll be overriding it.
#+begin_src emacs-lisp #+begin_src emacs-lisp
(after! idris-mode (after! idris-mode
(setq idris-interpreter-path "idris2" (setq idris-interpreter-path "idris2"
;; No fun allowed!! idris-repl-banner-functions
;; (The animated banner was messing with the popup system) '(idris-repl-text-banner) ; No fun allowed!!
idris-repl-banner-functions '(idris-repl-text-banner)
idris-repl-show-repl-on-startup nil ; Don't show repl on startup idris-repl-show-repl-on-startup nil ; Don't show repl on startup
) )
@ -3878,17 +4192,7 @@ Operators being in italics looks ugly in this mode too, but unfortunately due to
`(idris-semantic-postulate-face :foreground ,(doom-color 'yellow)))) `(idris-semantic-postulate-face :foreground ,(doom-color 'yellow))))
#+end_src #+end_src
** Yuck * Scratch
#+call: confpkg("Mode: Yuck")
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.
#+begin_src emacs-lisp :tangle packages.el
(package! yuck-mode)
#+end_src
* Scratch :noexport:
#+call: confpkg() #+call: confpkg()

View file

@ -2,6 +2,6 @@
# key: begin # key: begin
# name: latex-begin # name: latex-begin
# -- # --
\begin{${1:equation}*} \begin{${1:equation*}}
$0 $0
\end{$1*} \end{$1}