Contrary to what I previously thought, this doesn't affect indentation
itself, only how hard tab characters are rendered. Much of Emacs's
ecosystem assumes that hard tabs are 8 characters wide.
2024-07-23 01:45:33 -04:00
3 changed files with 240 additions and 232 deletions
@ -39,13 +39,13 @@ It was at this point that I started thinking about Emacs again. By chance, I hap
- Sensible defaults
- Sensible defaults
- Extensive ecosystem
- Extensive ecosystem
As I became more comfortable with configuration via scripting, I immersed myself into the many utilities that make up the Emacs ecosystem: =org-mode=, =calfw=, =calc=, =mu4e=. I started putting more and more time into tweaking these applications to fit my needs, my files kept getting longer and longer, and eventually I fully fell off the deep end and now we're here.
As I became more comfortable with configuration via scripting, I immersed myself into the many utilities that make up the Emacs ecosystem, especially =org-mode=, the gold standard of Emacs tools. I started putting more and more time into tweaking this environment to fit my needs, my files kept getting longer and longer, and eventually I fully fell off the deep end and now we're here.
*** Literate Programming
*** Literate Programming
My first Doom Emacs config was hacked together directly from the generated example config: no comments, no organization, nothing. ~after!~ and ~use-package!~ blocks were scattered about the file without rhyme or reason, making it very difficult to remember what any particular line of code was actually doing. I was able to mitigate some of this issue by categorizing my config into multiple files, but at the end of the day it was a losing battle. The config directory was at around 1200 lines of code before I decided that something needed to be done.
My first Doom Emacs config was hacked together directly from the generated example config: no comments, no organization, nothing. ~after!~ and ~use-package!~ blocks were scattered about the file without rhyme or reason, making it very difficult to remember what any particular line of code was actually doing. I was able to mitigate some of this issue by categorizing my config into multiple files, but at the end of the day it was a losing battle. The config directory was at around 1200 lines of code before I decided that something needed to be done.
I was considering what to do about this problem of organizational decay when I came across [[https://tecosaur.github.io/emacs-config/config.html][Tecosaur's config]] and learned about =org-mode='s literate programming support. I had been using =org-mode= for several months at this point and was very comfortable with it, so utilizing it to better organize my Emacs code seemed like a good idea.
I was considering what to do about this problem of organizational decay when I came across [[https://tecosaur.github.io/emacs-config/config.html][Tecosaur's config]] and learned about =org-mode='s literate programming support. I was very comfortable with =org-mode= at this point, so it seemed like a good solution and a natural next step.
**** Org and Source Code
**** Org and Source Code
@ -113,7 +113,7 @@ As part of their literate config, Tecosaur implemented =confpkg=, an embedded Em
- Reporting profiling information on config load times
- Reporting profiling information on config load times
It's an incredibly impressive utility, and I highly recommend reading [[https://tecosaur.github.io/emacs-config/config.html#rudimentary-configuration-confpkg][the section in their config]] on its design. I tried to read through it myself, but I don't understand half of it; it's a bizarre mixture of exploits to hook into =org-mode='s tangling process, self-modifying buffer shenanigans, and abuse of various features of the source block mechanism.
It's an incredibly impressive utility, and I highly recommend reading [[https://tecosaur.github.io/emacs-config/config.html#rudimentary-configuration-confpkg][the section in their config]] on its design. I tried to read through it myself, but I don't understand half of it; it's a bizarre mixture of exploits to hook into =org-mode='s tangling process, self-modifying buffer shenanigans, and abuse of various features of =org-babel=, the package that manages source blocks.
Luckily, I don't need to be able to understand code in order to do what I do best: press =Ctrl+C= and =Ctrl+V= in that order. Programming!
Luckily, I don't need to be able to understand code in order to do what I do best: press =Ctrl+C= and =Ctrl+V= in that order. Programming!
@ -122,7 +122,7 @@ If you're reading the raw org file instead of the published version, the code fo
- Prevent =confpkg='s code from being exported
- Prevent =confpkg='s code from being exported
- Change the package template to contain my information
- Change the package template to contain my information
- Reorganize to get rid of superfluous noweb references
- Reorganize to get rid of superfluous noweb references
- Hook into only babel calls that contain =confpkg= as a substring
- Hook only into babel calls that contain =confpkg= as a substring
- Allow package statements anywhere in subconfig files, rather than only at the beginning
- Allow package statements anywhere in subconfig files, rather than only at the beginning
** confpkg :noexport:
** confpkg :noexport:
@ -761,11 +761,19 @@ NODE defaults to the root node."
One of Doom Emacs's most useful features is its modular configuration system, allowing configuration code to be sectioned into modules that can be enabled or customized individually. Doom provides a full suite of prewritten modules to enable.
One of Doom Emacs's most useful features is its modular configuration system, allowing configuration code to be sectioned into modules that can be enabled or customized individually. Doom provides a full suite of prewritten modules to enable.
I'm a big fan of the Vertico ecosystem, as it's lightweight and easy to use. Let's turn on that module, along with the icons flag because why not.
I'm a big fan of the Vertico ecosystem, as it's lightweight and easy to use. Let's turn on that module, along with Corfu for a nice in-buffer completion popup and some icons because why not.
#+name: doom-completion
#+name: doom-completion
#+begin_src emacs-lisp
#+begin_src emacs-lisp
@ -862,7 +870,7 @@ ophints
;;tabs
;;tabs
(treemacs +lsp)
(treemacs +lsp)
unicode
unicode
(vc-gutter +diff-hl +pretty)
(vc-gutter +pretty)
vi-tilde-fringe
vi-tilde-fringe
(window-select +numbers)
(window-select +numbers)
workspaces
workspaces
@ -992,7 +1000,7 @@ emacs-lisp
;;hy
;;hy
idris
idris
;;json
;;json
;;(java +lsp)
(java +lsp +tree-sitter)
;;javascript
;;javascript
;;julia
;;julia
;;kotlin
;;kotlin
@ -1041,8 +1049,7 @@ This is mostly config settings that don't belong to any particular package and a
It wouldn't be Emacs if there wasn't an endless list of config variables to change every aspect of its function!
It wouldn't be Emacs if there wasn't an endless list of config variables to change every aspect of its function!
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(setq-default tab-width 2 ; 2 width tabs
(setq-default delete-by-moving-to-trash t ; Delete files to trash
delete-by-moving-to-trash t ; Delete files to trash
window-combination-resize t ; Resize windows more evenly
window-combination-resize t ; Resize windows more evenly
)
)
@ -1057,8 +1064,6 @@ It wouldn't be Emacs if there wasn't an endless list of config variables to chan
(global-subword-mode 1)
(global-subword-mode 1)
#+end_src
#+end_src
Thanks once again to Tecosaur for some of these settings.
** Personal Information
** Personal Information
#+call: confpkg()
#+call: confpkg()
@ -1074,7 +1079,7 @@ Emacs uses this basic personal information for a few different things, mostly ap
#+call: confpkg("Auth")
#+call: confpkg("Auth")
I don't want my cache files to get deleted whenever I mess up my Doom install, so let's move them to somewhere more safe.
The =auth-source-pass= module lets you use [[*Password Management][pass]] as a source for authentication. Let's turn that on.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(require 'auth-source-pass)
(require 'auth-source-pass)
@ -1193,25 +1198,9 @@ If PARENTS is non-nil, the parents of the specified directory will also be creat
:desc "Undo Tree"
:desc "Undo Tree"
"o u" #'undo-tree-visualize
"o u" #'undo-tree-visualize
:desc "Open URL"
:desc "Open URL"
"s u" #'goto-address-at-point)
"s u" #'goto-address-at-point
#+end_src
:desc "Nerd Icons"
"i i" #'nerd-icons-insert)
*** ... This is Also Here
I'm not even going to bother explaining this one. Emacs is just janky sometimes lol
@ -1223,7 +1212,7 @@ Some packages in this config such as =treemacs=, =org-roam=, etc. require certai
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 an instance 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 an instance of Python 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.
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:
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 simplest solution, but requires the most complex Emacs configuration.
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.
This section is an implementation of that third solution.
This section is an implementation of that third solution.
@ -1239,14 +1228,14 @@ If IMPURE is t, then allow impure builds."
This works well enough if we just want to build something, but there's a problem: we haven't indicated to Nix that we want this output to stick around, so it will be deleted the next time we garbage collect. To fix this, we can write a wrapper function that also makes the output path a garbage collection root.
This works if we just want to start a build, but there's a problem: we haven't indicated to Nix that we're using this output for something, so it will be deleted the next time we garbage collect. To fix this, we can write a wrapper function that also makes the output path a garbage collection root.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(defun nix-build-out-path-gcroot (name out &optional impure)
(defun nix-build-out-path-gcroot (name out &optional impure)
@ -2208,7 +2197,8 @@ Doom Emacs sets up spell-checking with ~ispell~ (Emacs-internal tool) and =aspel
(setq ispell-dictionary "en_US"))
(setq ispell-dictionary "en_US"))
#+end_src
#+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.
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.
Now that we have our new-and-improved template registry system, we can add new file templates as we please.
Now that we have our new-and-improved template registry system, we can add new file templates as we please.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
@ -2296,18 +2286,7 @@ A natural thing to do when browsing through the project tree is create a new fil
*** Project Integration
*** Project Integration
I often accidentally open the project tree before I've even selected a project, which I don't want because it messes up =treemacs-projectile=. Let's fix that problem:
When I have a project open, Treemacs is flexible and allows me to open directories other than that project. This /would/ be great and convenient if it weren't for the fact that it doesn't do so very well, often completely messing up the existing project structure. This convenience function ensures that only the project directory is open.
#+begin_src emacs-lisp
(defun ~/treemacs-restrict (&rest _)
(unless (doom-project-p)
(user-error "Must be in a project to open project tree")))
When I do have a project open, Treemacs is flexible and allows you to open directories other than that project. This /would/ be great and convenient if it weren't for the fact that it doesn't do so very well, often opening the wrong directories entirely. This convenience function ensures that only the project directory is open.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(defun ~/treemacs-fix-project ()
(defun ~/treemacs-fix-project ()
@ -2556,6 +2535,34 @@ I use the standard Unix-style password management system, [[https://www.password
For some unknown reason, the creators of the original =pass= package decided that when showing the pass buffer, the main dispatch function would call ~pop-to-buffer~ when it needs to be created, but ~switch-to-buffer~ when it already exists. These are different functions! Let's fix that.
When visiting a password file, the file's buffer replaces the pass buffer, which isn't very good UX. To fix this, we can advise it to use ~pop-to-buffer~, folding it into the popup system.
#+begin_src emacs-lisp
(defadvice! ~/pass-view ()
"Use `find-file-other-window' instead of `find-file'."
@ -2643,7 +2648,7 @@ four capitalized letters), and the cdr is a list of sub-tags.")
(lambda (subject)
(lambda (subject)
`((:startgrouptag) (,(car subject))
`((:startgrouptag) (,(car subject))
(:grouptags)
(:grouptags)
(,(format "{%s[[:digit:]]+}" (car subject)))
(,(format "{%s[[:digit:]]+L?}" (car subject)))
,@(cdr subject)
,@(cdr subject)
(:endgrouptag)))
(:endgrouptag)))
subject-alist)
subject-alist)
@ -2671,14 +2676,17 @@ There are a few useful functions Doom doesn't bind by default, so let's add them
(map! :after org
(map! :after org
:map org-mode-map
:map org-mode-map
:localleader
:localleader
"N" #'org-num-mode
"n" #'org-num-mode
"C" #'org-columns
"C" #'org-columns
"p" #'org-priority ; Remove extraneous commands
"g j" #'org-goto
"g j" #'org-goto
"c D" #'org-clock-display
"c D" #'org-clock-display
"m b f" #'org-table-eval-formula
"m b f" #'org-table-eval-formula
"m b F" #'org-table-edit-formulas
"m b F" #'org-table-edit-formulas
;; Remove extraneous commands
"a" #'org-attach
"p" #'org-priority
;; Map babel commands into localleader
;; Map babel commands into localleader
:desc "babel"
:desc "babel"
"v" (lookup-key org-mode-map (kbd "C-c C-v")))
"v" (lookup-key org-mode-map (kbd "C-c C-v")))
@ -2694,7 +2702,9 @@ While this is a complex problem, the solution is actually rather simple: just re
(map! :after org
(map! :after org
:map org-mode-map
:map org-mode-map
"TAB" nil
"TAB" nil
"<tab>" nil)
"<tab>" nil
:i "TAB" nil
:i "<tab>" nil)
#+end_src
#+end_src
This also means we don't need ~org-cycle~ to emulate indentation, which is nice. Unfortunately, this breaks something else: ~org-cycle~ not only handles visibility cycling, but also navigating inside tables. If ~org-cycle~ isn't being called when in insert mode, then we can't use =TAB= to move fields. This means that we need a little extra binding configuration:
This also means we don't need ~org-cycle~ to emulate indentation, which is nice. Unfortunately, this breaks something else: ~org-cycle~ not only handles visibility cycling, but also navigating inside tables. If ~org-cycle~ isn't being called when in insert mode, then we can't use =TAB= to move fields. This means that we need a little extra binding configuration:
@ -2734,6 +2744,16 @@ It's sometimes nice to be able to click a link in an Org file that takes me to o
** Appearance
** Appearance
*** Entities
#+begin_src emacs-lisp
(after! org
(setq org-pretty-entities t)
(pushnew! org-entities-user
'("top" "\\top" t "⊤" "" "22A4" "⊤")
'("bot" "\\bot" t "⊥" "" "22A5" "⊥")))
#+end_src
*** Modern
*** Modern
Doom Emacs's =+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=.
Doom Emacs's =+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=.
@ -2778,6 +2798,7 @@ Doom Emacs's =+pretty= flag by default uses the package =org-superstar= to prett
("email" . "")
("email" . "")
("date" . "")
("date" . "")
("property" . "")
("property" . "")
("startup" . "")
("filetags" . "")
("filetags" . "")
("bind" . "")
("bind" . "")
("bibliography" . "")
("bibliography" . "")
@ -2867,7 +2888,10 @@ Since we've disabled =+pretty=, we need to add the packages we do want from it,
(lambda (a b) (funcall time-subtract-old (or a org-todo-time) (or b org-todo-time))))
(decode-time-old (symbol-function #'decode-time))
((symbol-function #'decode-time)
(lambda (&optional time &rest args)
(apply decode-time-old (or time org-todo-time) args))))
(apply old-fn args))
(apply old-fn args)))
#+end_src
#+end_src
We can then define and bind alternate versions of ~org-todo~ and ~org-agenda-todo~ that allow us to pick the time to set.
We can then define and bind alternate versions of ~org-todo~ and ~org-agenda-todo~ that allow us to pick the time to set.
@ -3148,40 +3187,6 @@ If nil, then `default-directory' for the org buffer is used.")
(funcall orig-fn extension subtreep pub-dir))
(funcall orig-fn extension subtreep pub-dir))
#+end_src
#+end_src
*** Attachment Inline Previews
Doom enhances Org mode's attachment system to show inline previews of attached images. However, this appears to be broken due to using outdated APIs, so we have to patch the link parameter responsible.
#+begin_src emacs-lisp
(defadvice! ~/org-image-file-data-fn (protocol link _description)
Inline images in Org are file links pointing to images without a description. User-defined inline images can have a description, however, which makes for a disparity between file links and other links that is very counter-intuitive. We can advise the image data provider functions to fix this, and I'll add a variable to restore the default behavior if I ever want to.
Inline images in Org are file links pointing to images without a description. User-defined inline images can have a description, however, which makes for a disparity between file links and other links that is very counter-intuitive. We can advise the image data provider functions to fix this, and I'll add a variable to restore the default behavior if I ever want to.
@ -3193,9 +3198,8 @@ Inline images in Org are file links pointing to images without a description. Us
(defadvice! +org-inline-desc (old-fn protocol link description)
(defadvice! +org-inline-desc (old-fn protocol link description)
"Disable inline images with descriptions when `+org-inline-image-desc'
"Disable inline images with descriptions when `+org-inline-image-desc'
@ -3327,19 +3331,17 @@ 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's... "easy" to patch Org-mode's category system. 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
[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!
** Org Roam
** Org Roam
#+call: confpkg("Org: Roam")
#+call: confpkg("Org: Roam")
@ -3355,21 +3357,23 @@ Org-roam is inspired by Roam Research, and like that tool it is based on the Zet
In my use of Org-roam for task management, I divide nodes into a few different categories:
In my use of Org-roam for task management, I divide nodes into a few different categories:
1.*Areas*, which represent continual areas of your life to organize and plan;
1.*Areas*, which represent continual areas of your life to organize and plan;
2.*Goals*, short- or long-term, things that can be completed;
2.*Goals*, short- or long-term that can be completed;
3.*Tasks*, which are one-time and contribute to goals or areas.
3.*Tasks*, which are small, one-time and contribute to goals or areas.
Areas are stored as subnodes of the =Areas= file node, and likewise for goals. They also have the =:area:= and =:goal:= tags respectively. A task is a node that is a TODO entry that links to an area or a goal. We can thus check for if a node is a task by checking if the node links to a =:area:= or =:goal:= tagged node.
Areas are stored as subnodes of the =Areas= file node, and likewise for goals. They also have the =:area:= and =:goal:= tags respectively. A task is a node that is a TODO entry that links to an area or a goal. We can thus check for if a node is a task by checking if the node links to a =:area:= or =:goal:= tagged node.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(defun ~/org-roam-get-linked-nodes (node tag)
(defun ~/org-roam-get-linked-nodes (node tag)
"Return the nodes that NODE links to that are tagged with TAG."
"Return the nodes that NODE links to that are tagged with TAG."
@ -3460,7 +3465,7 @@ This advice overrides ~org-capture-place-template~, the function that:
2. Modifies the template to fit its context
2. Modifies the template to fit its context
3. Places the template in the file
3. Places the template in the file
The advice temporarily sets Org's capture template to an empty string, lets Org do its thing, and then expands the template itself. This does mean that Org no longer performs point 2, but YASnippet is powerful enough that we can simply do that ourselves in the template.
The advice temporarily sets Org's capture template to an empty string, lets Org do its thing, and then expands the template itself. This does mean that Org can no longer perform point 2, but YASnippet is powerful enough that we can simply do that ourselves in the template.
*** Roam Capture
*** Roam Capture
@ -3484,17 +3489,22 @@ 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. However, when I'm explicitly telling Roam to make a new node, it would be a shame if there was only one option. To fix this, I'll add new variables representing the /default/ templates to use in situations where I don't want to have to choose.
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
#+begin_src emacs-lisp
(defvar org-roam-capture-default-template nil
(defvar org-roam-capture-default-template nil
"The default capture template to use when not explicitly capturing.")
"The default capture template to use when not explicitly capturing.")
(defvar org-roam-capture-existing-templates nil
"Capture templates to use when capturing on an existing node.")
"The default daily capture template to use when not explicitly capturing.")
"The default daily capture template to use when not explicitly capturing.")
#+end_src
#+end_src
(We don't distinguish between new and existing dailies, since dailies are meant to follow a single format, which can just be covered by the default template.)
With those variables created, we can define our templates.
With those variables created, we can define our templates.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
@ -3512,26 +3522,12 @@ With those variables created, we can define our templates.
"#+title: %<%Y-%m-%d>")
"#+title: %<%Y-%m-%d>")
:unnarrowed t
:unnarrowed t
:immediate-finish t))
:immediate-finish t))
;; Active templates
;; new-file templates
(setq org-roam-capture-templates
(setq org-roam-capture-templates
'(("f" "Standalone file" plain ""
'(("f" "Standalone file" plain ""
:target (file+head "${file-maybe-dir}"
:target (file+head "${file-maybe-dir}"
"#+title: ${title}")
"#+title: ${title}")
:unnarrowed t)
:unnarrowed t))
("n" "Citation Note" plain ""
:target (file+head "${file-maybe-cite}"
"#+title: ${note-title}")
:immediate-finish t
:unnarrowed t)
("h" "Heading" entry
"`(make-string (org-outline-level) ?*)`* ${title}
:RELATED:
Subnote of `(let ((node (org-roam-node-at-point)))
(format \"[[id:%s][%s]]\"
(org-roam-node-id node)
(org-roam-node-title node)))`$1
:END:\n\n$0"
:target (file "20240329114914-a.org")))
org-roam-dailies-capture-templates
org-roam-dailies-capture-templates
'(("e" "Event" entry "* $1\n%^t\n\n$0"
'(("e" "Event" entry "* $1\n%^t\n\n$0"
:target (file+head "%<%Y-%m-%d>.org"
:target (file+head "%<%Y-%m-%d>.org"
@ -3553,10 +3549,22 @@ Now we just have to advise Org-roam with the proper logic!
This "overview" agenda command is very nice. It's so nice, in fact, that it's almost always the only agenda view I want to use! Having to go through the dispatcher every time I want to access it is annoying and unnecessary.
This "overview" agenda command is very nice. It's so nice, in fact, that it's almost always the only agenda view I want to use! Having to go through the dispatcher every time I want to access it is annoying and unnecessary.
@ -3683,22 +3710,22 @@ This "overview" agenda command is very nice. It's so nice, in fact, that it's al
"o A" #'org-agenda)
"o A" #'org-agenda)
#+end_src
#+end_src
*** Agenda Files
*** TODO Tweaks
I have a lot of different subdirectories and groupings in my org directory, but unfortunately directories listed in ~org-agenda-files~ aren't checked recursively! We can solve this by creating a function ~org-agenda-files-function~ to return the agenda files, then advising Org to call that function before getting the agenda files.
When finding a node with ~org-roam-node-find~, the universal argument to open it in another window doesn't work when the node is new. We can fix this with an override:
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(defun org-agenda-files-function ()
(cl-defun ~/org-roam-node-find (&optional other-window initial-input filter-fn pred &key templates)
@ -3813,17 +3840,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")
@ -3914,7 +3930,7 @@ The Doom module is very outdated, so I'll be overriding it.
*** Appearance
*** Appearance
Operators being in italics looks ugly in this mode too, but unfortunately due to Idris's more complicated syntax highlighting system we have to do a bit more work than for Haskell. There's also the issue of the semantic highlighting faces, which don't match our theme colors.
Operators being in italics looks ugly in this mode too, but due to Idris's more complicated syntax highlighting system we have to do a bit more work than for Haskell. There's also the issue of the semantic highlighting faces, which don't match our theme colors.
#+begin_src emacs-lisp
#+begin_src emacs-lisp
(after! idris-mode
(after! idris-mode
@ -3929,6 +3945,45 @@ Operators being in italics looks ugly in this mode too, but unfortunately due to
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
** Java
The =lsp-java= package provides LSP support using the standard language server, the Eclipse-based ~jdtls~. Unfortunately, this package isn't designed for Nix, so it fails to find the server script in our Nix store. We need to do a bit of legwork to patch in this support, as well as some extra considerations, such as per-project config directories.
#+begin_src emacs-lisp
(defun +lsp-java-server-store-path ()
"Return the Nix store derivation path to the Java language server."
(string-remove-suffix
(concat "/bin/" lsp-java-jdt-ls-command)
(or
(executable-find lsp-java-jdt-ls-command)
(user-error "Could not find Java language server"))))