Finishing denote-data - new-note hook and async cache with user config
- YT :: https://www.youtube.com/watch?v=mBAhOCyYKlc
- Original title :: Emacs: continuing 'denote' maintenance and refinements
Live stream continuing work on denote-data-mode, the opt-in in-memory cache for Denote. Prot makes the recently added cache functions and abnormal hooks public, then adds a new hook that runs when denote creates a note. This lets the cache drop after-save-hook and register notes before they're saved, including links inserted by templates. He then fixes the asynchronous cache builder so the child Emacs process gets the user's relevant Denote options. By the end he considers the core feature complete, pending battle-testing and ERT tests. A large part of the stream is chat Q&A: Denote vs. org-roam, democracy, geopolitics (EU expansion, Russia, Ukraine), Trump, and defvar vs. defcustom.
Making the internals public
- Emacs convention:
package--name(double dash) is private, so don't rely on it. - The cache's remove-file and post-rename-update functions, and the abnormal hooks for "link to missing file" and "after rename" added last stream, become public so other packages can use them. A public hook implies its function should be public too, since users will run it.
- No obsolete aliases are needed (
define-obsolete-function-alias) because the code hasn't been in a stable release yet. - Renaming is done with his
substitutepackage. It reports the match count, which he uses as a sanity check that no names collide.
New-note hook instead of after-save-hook
- The
denotefunction, which every note-creating command calls, now runs a new abnormal hook. It passes the note's identifier, title, signature, keywords (a list) and path. denote-data-modeadds a function to this hook to insert the new note into the hash table. Theafter-save-hookapproach is removed, which also resolves a to-do aboutsave-some-buffers.- Problem: the file doesn't exist yet when the hook runs, so reading contents with
insert-file-contentsin a temp buffer errors. Fix: the contents reader takes an optional file contents string. The caller gets it viaget-file-buffer+buffer-string, so links inserted by adenote-templateare cached immediately. - Tested with a template that links to an earlier note:
denote-data-getreturns the entry with the link list populated.
Async cache build with the user's configuration
- Benchmark: rebuilding the cache for ~1,500 notes five times took ~5 s (51 GCs), about a second per thousand files. That's fast, but he still builds it asynchronously so Emacs never blocks.
- The async path spawns an external Emacs process. Bug: it loaded Denote's defaults, ignoring the user's
denote-directoryand other options. - He picked the relevant options by going through the defcustoms: directory, file-name components order, file type, excluded files and directories regexps, file-name slug functions, and similar.
- A helper "read the user configuration" builds a
setqform string. Each value is formatted via a lambda that adds a quote for non-nil lists and symbols. Lessons along the way: a stale.elcwas being loaded, there was a missing paren, andnewlineshouldn't be used programmatically because it does too much (electric, read-only checks). - Verified with a temporary
denote-directoryof two notes: the async build fills the hash table correctly. - He's never seen a canonical way to call a child Emacs from Emacs and couldn't find one in the manual, so this is his own approach.
- To-do: let other packages and users extend the captured config (e.g.
denote-journal-directory,denote-silodirectories) via an options alist of(user-option . value), set before enabling the mode or followed by a cache rebuild.
Status
- He sees no known gaps: notes are cached on creation, updated on rename, and stale entries are removed when a link target is missing.
- The new hooks and function variables are reusable abstractions, e.g. for a database-backed Denote integration.
- Remaining: battle-testing, reviewing that prompt affixation helpers consistently
funcallthe configurable title/identifier functions, and ERT tests.
Chat Q&A highlights
- org-roam → Denote: Denote doesn't depend on Org. Prot's website uses Jekyll/Markdown (~3,000 posts since 2016), and Denote's naming scheme works for any file, e.g. his MKV recordings. Consistent names give predictability, which gives retrievability with any tool, not just Emacs.
- Icons in Dired: a small private gimmick. Use a regular icon package instead.
- Simulation theory: irrelevant. Our presence is shaped by its parameters either way. Meaning comes from finding a reason to struggle against entropy.
- Equal rights regardless of IQ: yes. Who would decide, and by which criteria? Smart people also lack lived experience (e.g. not affording vacations). Democracy exposes the diversity of lived experience. Today's systems are oligarchies with democratic elements.
- Geopolitics: there are no good guys. Geography imposes non-negotiable ("peremptory") incentives. The EU went from a coal-and-steel trade cartel to a political-union ambition (Maastricht 1993) and expanded east, which Russia sees as a security threat (the Black Sea). These are competing imperialisms, with the EU acting as NATO's political arm on Ukraine. Free trade is the best guard against war, because trading partners lose more by fighting.
- Trump: effective at getting things (Venezuelan oil, Greenland), but he governs like reality TV. Prot's broader point: decoupling people from the consequences of war (contractors, drones, memes, no declared wars) removes accountability.
- Multi-file packages: harder, because of load order and scope. He merged
denote.elinto one file. Navigating large files is easy withconsult-outline+embark-collect. - Elisp course: see his free book Emacs Lisp Elements (protesilaos.com/emacs/emacs-lisp-elements).
- defvar vs. defcustom vs. defgroup:
defvardeclares a variable without stating intent.defcustomdeclares it safe for users to configure, with a type, and commits the developer to support all valid values.defgrouporganizes options and can carry metadata such as a manual link.