Compare commits

..

No commits in common. "17bd8557db1c28d2edb4f43156c6fe7bac7df707" and "e2ae7b07361893d00e6fc0c72060dcb71e2b391c" have entirely different histories.

3 changed files with 92 additions and 160 deletions

View file

@ -67,22 +67,22 @@ 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, but also prevent the file from being interpreted as a program, which is the important part! 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 codebase, storing the code inside source blocks in the document. This could be done by maintaining 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, which involves two parallel processes:
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 done 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, which involves two processing systems:
1. /Tangling/, converting the file into raw source code. Tangling must be performed before the code is used; in Org, code is tangled into a new file, and this can be done to generate many source files from a single Org file.
1. /Tangling/, converting the file into raw source code. Tangling must be performed before the code is used; in Org, code is tangled into a new file, and this can be done to generate many source files at once.
2. /Weaving/, formatting the file into a human-readable document. Org conceptually divides this further into "exporting" and "publishing," where the latter is intended specifically for converting into web-ready HTML.
You are currently reading the published version of this literate program[fn:1]. If you were to download this repository and use it as your config, Emacs would be running the tangled version. These versions are generated in separate processes, but both are ultimately derived from the content and metadata inside of the Org file.
[fn:1] Unless you're reading the raw file on Github, in which case you are probably already decently familiar with =org-mode= to be able to read its markup.
**** Code and Comprehension
**** From Code into Comprehension
The simultaneous handling of documentation and code inherent to literate programming is reminiscent of documentation generation (doc comments) in traditional programming. Both systems involve superimposing code and documentation into one file, but the literate style takes the concept one step further; the document isn't embedded in the code, the code is embedded in the document.
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 literate macros, features intended to break one's code out of the restrictions of standard programming.
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 literate macros and tangling configuration, features intended to break one's code out of the restrictions of standard programming.
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 into categories.
@ -119,10 +119,9 @@ Luckily, I don't need to be able to understand code in order to do what I do bes
If you're reading the raw org file instead of the published version, the code for =confpkg= is below. It is mostly unchanged, aside from these tweaks:
- Prevent =confpkg='s code from being exported
- Change the package template to contain my information
- Reorganize to get rid of superfluous noweb references
- Hook into only babel calls that contain =confpkg= as a substring
- Prevent the code from being exported
- Allow package statements anywhere in subconfig files, rather than only at the beginning
** confpkg :noexport:
@ -136,10 +135,8 @@ If you're reading the raw org file instead of the published version, the code fo
(message "Intitialising confpkg")
(org-fold-core-ignore-fragility-checks
(org-babel-map-executables nil
(let ((context (org-element-context)))
(and (eq (org-element-type context) 'babel-call)
(string-match-p "confpkg" (org-element-property :call context))
(org-babel-lob-execute-maybe))))))
(when (eq (org-element-type (org-element-context)) 'babel-call)
(org-babel-lob-execute-maybe)))))
(quit (revert-buffer t t t)))
#+end_src
@ -1002,7 +999,7 @@ idris
;;lua
markdown
;;nim
(nix +lsp +tree-sitter)
(nix +tree-sitter)
;;ocaml
(org +roam2 +present
+gnuplot +jupyter
@ -1190,8 +1187,6 @@ If PARENTS is non-nil, the parents of the specified directory will also be creat
#+begin_src emacs-lisp
(map! :leader
:desc "Undo Tree"
"o u" #'undo-tree-visualize
:desc "Open URL"
"s u" #'goto-address-at-point)
#+end_src
@ -1322,7 +1317,7 @@ I have rather specific tastes when it comes to line wrapping. I like soft line w
(setq +word-wrap-fill-style 'soft ; Soft line wrapping
evil-respect-visual-line-mode t ; Respect visual line mode
)
(setq-default fill-column 100) ; More space before wrap
(setq-default fill-column 90) ; More space before wrap
#+end_src
*** Hacks
@ -1455,25 +1450,7 @@ Everything else goes in ~config.el~, which is managed by [[*=confpkg=][confpkg]]
;; I don't really use TAB very often, so prefer other actions
(setq +corfu-want-tab-prefer-navigating-snippets t
+corfu-want-tab-prefer-expand-snippets t
+corfu-want-tab-prefer-navigating-org-tables t
+corfu-want-minibuffer-completion nil
corfu-min-width 25))
#+end_src
Before switching to Corfu, I got really used to Doom's omnicomplete bindings for various company backends. Doom unfortunately doesn't do this for CAPFs, so we'll have to do the work ourselves.
#+begin_src emacs-lisp
(map! :prefix "C-x"
:i "C-e" #'cape-elisp-symbol
:i "C-f" #'cape-file
:i "C-l" #'cape-line
:i "C-]" #'complete-tag
:i "s" #'cape-dict
:i "C-s" #'yasnippet-capf
:i "C-d" #'cape-dabbrev
:i "\\" #'cape-tex)
+corfu-want-tab-prefer-navigating-org-tables t))
#+end_src
** Eldoc
@ -1990,12 +1967,9 @@ Having an IDE-style tooltip pop up is nice, but ~flymake-popon~ is a bit ugly by
I use GPG signing for commits, which means that committing often takes longer than the single second timeout. Eight seconds seems like a reasonable amount of time to type in a password.
Let's also increase the maximum length of commit summaries past the default of 50 characters, because that's a very restrictive limit.
#+begin_src emacs-lisp
(after! git-commit
(setq git-commit-post-finish-hook-timeout 8
git-commit-summary-max-length 60))
(setq git-commit-post-finish-hook-timeout 8))
#+end_src
*** Magit Syntax Highlighting
@ -2168,35 +2142,6 @@ Doom's command to create a new snippet, ~+snippets/new~, defines a template insi
(find-file snippet-file-name))))
#+end_src
*** File Templates
Doom's =file-templates= module extends =yasnippet= to provide a nice file template system. The idea is simple: the variable ~+file-templates-alist~ maps file predicates to snippets. If a file that matches a predicate is created, the corresponding snippet is automatically expanded inside of it.
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 advise 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
(defadvice! ~/file-templates-set (pred plist)
:override #'+file-templates--set
(if (plist-member plist :remove)
(setq +file-templates-alist
(assoc-delete-all pred +file-templates-alist))
(setq +file-templates-alist
(nconc +file-templates-alist `((,pred ,@plist))))))
#+end_src
Now that we have our new-and-improved template registry system, we can add new file templates as we please.
**** 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.
#+begin_src emacs-lisp
(set-file-templates!
'(nix-mode :remove t)
'("/etc/nixos/.*\\.nix$" :mode nix-mode)
'("/flake\\.nix$" :trigger "__flake.nix" :mode nix-mode))
#+end_src
** Spell Checking
#+call: confpkg("Pkg: ispell")
@ -2209,7 +2154,6 @@ Doom Emacs sets up spell-checking with ~ispell~ (Emacs-internal tool) and =aspel
#+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.
Now that we have our new-and-improved template registry system, we can add new file templates as we please.
#+begin_src emacs-lisp
(defvar ~/plaintext-dict (expand-file-name "ispell/dict.plain" doom-data-dir)
@ -2284,16 +2228,6 @@ Treemacs is a really useful package, but it also has a lot of defaults I don't l
)
#+end_src
*** Bindings
A natural thing to do when browsing through the project tree is create a new file. The best way to do this is with the usual ~find-file~ command, bound to =SPC .=, but it would be nice to have a binding to save that leader key press.
#+begin_src emacs-lisp
(map! :after treemacs
:mode treemacs-mode
"." #'find-file)
#+end_src
*** 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:
@ -2548,7 +2482,7 @@ I use the standard Unix-style password management system, [[https://www.password
:desc "Password Store"
"o s" #'pass)
(after! pass
(after! password-store
(setq pass-show-keybindings nil ; Keybindings take up too much space
pass-suppress-confirmations t ; Quit shouldn't need a confirm step
)
@ -2571,7 +2505,9 @@ Unfortunately, with that power comes a *lot* of configuration work up-front. It
(setq org-directory "~/org/")
(after! org
(setq org-cycle-emulate-tab nil ; We don't need this with evil
(setq org-archive-location ; Global archive file
(concat org-directory ".org_archive::")
org-cycle-emulate-tab nil ; We don't need this with evil
org-attach-dir-relative t
org-log-into-drawer t ; Write logs into :LOGBOOK:
org-footnote-auto-label 'confirm ; Allow editing of footnote names
@ -2697,7 +2633,7 @@ While this is a complex problem, the solution is actually rather simple: just re
"<tab>" nil)
#+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. This means that we need a little extra binding configuration so that we can make this properly work:
#+begin_src emacs-lisp
(let ((item
@ -2754,6 +2690,8 @@ Doom Emacs's =+pretty= flag by default uses the package =org-superstar= to prett
org-modern-list '((?- . "•")
(?+ . "•")
(?* . "•"))
org-modern-footnote
(cons nil (cadr org-script-display))
org-modern-todo nil
org-modern-todo-faces
'(("TODO" :inverse-video t :inherit org-todo)
@ -2764,11 +2702,11 @@ Doom Emacs's =+pretty= flag by default uses the package =org-superstar= to prett
("KILL" :inverse-video t :inherit +org-todo-cancel))
org-modern-block-fringe nil
org-modern-block-name
'(("src" "»" "«")
'((t . t)
("src" "»" "«")
("example" "»–" "–«")
("quote" "❝" "❞")
("export" "⏩" "⏪")
(t . t))
("export" "⏩" "⏪"))
org-modern-priority nil
org-modern-horizontal-rule (make-string 36 ?─)
org-modern-keyword
@ -2850,7 +2788,7 @@ The default colors for various elements of =org-modern= don't match with our the
:background ,(doom-color 'bg-alt)
:foreground ,(doom-color 'fg-alt))
`(org-modern-progress-complete
:background ,(doom-color 'base5)
:background ,(doom-color 'yellow)
:foreground ,(doom-color 'bg-alt))))
#+end_src
@ -2890,9 +2828,7 @@ Org uses many popup windows for various things, and we can use Doom to make them
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!
*** Archiving
**** Archive Restore
*** Archive Restore
Org offers a wonderfully useful trash system called archiving, which lets you move subtrees into a file where they can be saved without permanently deleting them. However, there's no system for automatically restoring these subtrees once they're archived!
@ -2911,7 +2847,7 @@ the attachment cannot be restored!"
(interactive)
(let* ((archived-p
(lambda (element)
(and (eq (org-element-type element) 'headline)
(and (org-element-type-p element 'headline)
(org-element-property :ARCHIVE_FILE element))))
(lineage (org-element-lineage (org-element-at-point) nil t))
(heading (or (-first archived-p lineage)
@ -2946,32 +2882,6 @@ the attachment cannot be restored!"
(message "Restored \"%s\" to file %s" title file)))
#+end_src
**** File Archive
Currently, there only exists capabilities to archive a subtree, not an entire file. This matters for me because I often want to remove Org Roam files so that they don't clutter up the database and bog down my agenda. As a quick hacky way to implement this, we can just rename the file to make it look like an archive file.
#+begin_src emacs-lisp
(defun org-archive-file (file)
"Archive the entire Org file FILE by renaming it to an org_archive file.
Interactively, this operates on the current buffer. With a prefix arg,
prompt for a file instead."
(interactive (list (if current-prefix-arg
(read-file-name "Archive file: " nil nil t)
(buffer-file-name (buffer-base-buffer)))))
(find-file file)
(widen)
(let ((archive (car (org-archive--compute-location org-archive-location))))
(if (file-exists-p archive)
(progn
(goto-char (point-min))
(insert "\n# Final contents:\n\n")
(append-to-file (point-min) (point-max) archive))
(add-file-local-variable-prop-line 'mode 'org)
(write-file archive))
(doom/delete-this-file nil t)))
#+end_src
*** Todo Date Overriding
My attention span being what it is, I often forget to update TODO entries in my Org files until long after the task has been completed. I rely heavily on tracking TODOs through timestamps, so it would be nice to have a command to specify the time to log the task as being completed at. To do this, we can create a new variable ~org-todo-time~ that will override the time when set.
@ -3097,7 +3007,7 @@ The simple package =org-checklist= from =org-contrib= makes it so that checkboxe
#+begin_src emacs-lisp
(when (modulep! :lang org)
(use-package! org-checklist
(use-package org-checklist
:commands (org-reset-checkbox-state-maybe
org-make-checklist-export
org-checklist)
@ -3111,7 +3021,11 @@ The simple package =org-checklist= from =org-contrib= makes it so that checkboxe
The ~org-checklist~ function will reset the checkboxes on any task, but I only want them reset when the task repeats.
#+begin_src emacs-lisp
(advice-add #'org-checklist :before-while #'org-get-repeat)
(defadvice! ~/org-checklist-only-on-repeating (old-fn)
"Only reset checkboxes when marking repeater tasks as DONE."
:around #'org-checklist
(when (org-get-repeat)
(funcall old-fn)))
#+end_src
I don't want to have to specify the =RESET_CHECK_BOXES= property for every TODO I write, though. I would much prefer if it was on by default, and the system allowed me to turn it off if I wanted to. Luckily, the fine control Org gives you over property inheritance nicely fixes this problem.
@ -3129,6 +3043,47 @@ I don't want to have to specify the =RESET_CHECK_BOXES= property for every TODO
** Bug Fixes and Tweaks
*** Improved Tag Selection
The facilities for selecting and adding tags do not play very nicely with complex tag hierarchies, especially fast tag selection. The main problems are:
- Fast tag selection interprets regex tags as actual tags
- Fast tag selection can assign multiple keys to the same tag
- Regular tag selection interprets special tag entries such as ~:startgroup~ as actual tags
To fix this, we'll need some liberal use of override advising.
#+begin_src emacs-lisp
(defadvice! ~/org-assign-fast-keys (alist)
:override #'org-assign-fast-keys
(let (new e (alt ?0))
(while (setq e (pop alist))
(if (or (memq (car e) '(:newline :grouptags :endgroup :startgroup :startgrouptag :endgrouptag))
(and (string-prefix-p "{" (car e)) (string-suffix-p "}" (car e)))
(cdr e)) ;; Key already assigned.
(push e new)
(let ((clist (string-to-list (downcase (car e))))
(used (append new alist)))
(when (= (car clist) ?@)
(pop clist))
(while (and clist (rassoc (car clist) used))
(pop clist))
(unless clist
(while (rassoc alt used)
(cl-incf alt)))
(push (cons (car e) (or (car clist) alt)) new))))
(nreverse new)))
#+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.
#+begin_src emacs-lisp
(after! org
(setq org--fast-tag-selection-keys
(string-to-list "asdfghjklrueitywovnASDFGHJKLRUEITYWOVN")
org-fast-tag-selection-maximum-tags 40))
#+end_src
*** Export Directory
Org mode by default exports to the same directory the org-mode file is in. This is inconvenient for me, as I use a lot of subdirectories. To fix this, we can advise the function ~org-export-output-file-name~.
@ -3581,7 +3536,7 @@ 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-sorting-strategy
'((agenda time-up habit-down priority-down category-up)
'((agenda time-up habit-down prority-down category-up)
(todo habit-down priority-down time-up category-up)
(tags habit-down priority-down time-up category-up)
(search category-up))
@ -3592,10 +3547,10 @@ A full week-long agenda is usually too cluttered for me to read, so I'll narrow
;; Agenda prefix
org-agenda-prefix-format
'((agenda . " %i %-36:c%?-12t% s")
(todo . " %i %-36:c")
(tags . " %i %-36:c")
(search . " %i %-36:c"))))
'((agenda . " %i %-18:c%?-12t% s")
(todo . " %i %-18:c")
(tags . " %i %-18:c")
(search . " %i %-18:c"))))
#+end_src
*** Agenda View
@ -3619,6 +3574,7 @@ The Org agenda is a very nice feature, but by default it doesn't really provide
(setq org-super-agenda-header-map nil))
#+end_src
The ~org-agenda~ dispatcher is occasionally useful, but most of the time when I want to open my agenda, it's to see my "preferred" view.
By customizing ~org-super-agenda-groups~ via a let-binding in my custom agenda view, I can set up a sophisticated multi-category agenda system.
#+begin_src emacs-lisp
@ -3641,30 +3597,23 @@ By customizing ~org-super-agenda-groups~ via a let-binding in my custom agenda v
((org-super-agenda-groups
'((:name "Next/In Progress"
:todo ("NEXT" "STRT" "WAIT" "HOLD"))
(:discard
(:date t
:deadline t
:scheduled t))
(:name "Important"
:priority "A"
:order 1)
(:name "Goals"
:tag "goal"
:order 9)
(:name "Notes to Intake"
:tag "notes"
:order 10)
(:name "Assignments"
:tag "assign"
:order 2)
(:order-multi
(3
(:auto-map (lambda (item)
(~/org-agenda-section-by-link
"Goal: " "goal" item)))
"Goal: " "goal" item))
:order 3)
(:auto-map (lambda (item)
(~/org-agenda-section-by-link
"Area: " "area" item))))))))))))))
"Area: " "area" item))
:order 4))))))))))
#+end_src
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.
@ -3722,7 +3671,7 @@ Let's start with some configuration. I use [[https://www.zotero.org/][Zotero]] t
*** Citation Settings
I primarily use the CSL export processor to create MLA-style citations, so let's configure that to make its citations more standard.
I primarily use the CSL export processor to create MLA-style citation, so let's configure that to make its citations more standard.
#+begin_src emacs-lisp
(after! org
@ -3880,7 +3829,7 @@ The Doom module is very outdated, so I'll be overriding it.
idris-repl-show-repl-on-startup nil ; Don't show repl on startup
)
;; (add-hook! idris-mode :append #'lsp!)
(add-hook! idris-mode :append #'lsp!)
(set-repl-handler! 'idris-mode (cmd! (idris-repl-buffer)))
(set-lookup-handlers! 'idris-mode
@ -3946,9 +3895,9 @@ Since I've started using [[https://github.com/elkowar/eww/tree/master?tab=readme
This section is for code with little or no associated documentation. This could be because the code is:
1. Temporary
2. Self-explanatory
3. Hard to categorize
4. Just not really worth the time it takes to write commentary
2. Unimportant
3. Self-explanatory
4. Just not really worth the time it takes to write these explanations
** Org

View file

@ -1,17 +0,0 @@
# -*- mode: snippet -*-
# contributor: Kiana Sheibani <kiana.a.sheibani@gmail.com>
# key: __flake.nix
# name: nix flake template
# --
{
description = "${1:Flake description}";
inputs = {
${2:nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
}${3:flake-utils.url = "github:numtide/flake-utils";
}$4
};
outputs = { self$5, ... }:
$0
}

View file

@ -1,5 +1,5 @@
# -*- mode: snippet -*-
# contributor: Kiana Sheibani <kiana.a.sheibani@gmail.com>
# contributor: Kiana Sheibani
# key: __
# name: Snippet template
# --